Skip to content

Commit

Permalink
Runtime Benchmarks
Browse files Browse the repository at this point in the history
- Added QR and Micro QR
Rendering
- Added a simple function for rendering micro qr codes as an image
  • Loading branch information
lessthanoptimal committed Jan 24, 2022
1 parent c6f9adf commit 22eb1fa
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 12 deletions.
3 changes: 1 addition & 2 deletions change.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ Version : 0.41
- Much stricter and more accurate test that looks for UTF-8 strings in BYTE data
- Config has defaultEncoding which is the encoding it uses if it decides BYTE isn't UTF-8
- Added an example demonstrating how raw data can be used ExampleQrCodeRawData
- Added JMH Benchmarks for regression testing
- SLAM
- TODO Monocular
- TODO Stereo
- Calibration
- TODO calibration targets Hamming Marker grid and chessboard, e.g. ChArUco and ArUco grids.
- Testing
- TODO make sure Micro QR and QR are in JMH benchmarks
- Concurrency
- Added threaded KLT tracker
- Regression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@

package boofcv.examples.fiducial;

import boofcv.alg.drawing.FiducialImageEngine;
import boofcv.alg.fiducial.microqr.MicroQrCode;
import boofcv.alg.fiducial.microqr.MicroQrCodeEncoder;
import boofcv.alg.fiducial.microqr.MicroQrCodeGenerator;
import boofcv.gui.image.ShowImages;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.struct.image.GrayU8;

import java.awt.image.BufferedImage;

Expand All @@ -43,20 +43,16 @@ public static void main( String[] args ) {
addAutomatic("Test ん鞠").fixate();
// NOTE: The final function you call must be fixate(), that's how it knows it's done

// FiducialImageEngine is an interface for rending to images. You can also render to PDF and other formats.
var render = new FiducialImageEngine();
render.configure(/* border */ 10, /* width */ 500);

// Render the marker after configuring the generator
new MicroQrCodeGenerator().setMarkerWidth(500).setRender(render).render(qr);
// Render the QR as an image. It's also possible to render as a PDF or your own custom format
GrayU8 rendered = MicroQrCodeGenerator.renderImage(/* pixel per module */ 10, /* border modules*/ 1, qr);

// Convert it to a BufferedImage for display purposes
BufferedImage image = ConvertBufferedImage.convertTo(render.getGray(), null);
BufferedImage output = ConvertBufferedImage.convertTo(rendered, null);

// You can also save it to disk by uncommenting the line below
// UtilImageIO.saveImage(image, "microqr.png");
// UtilImageIO.saveImage(output, "microqr.png");

// Display the image
ShowImages.showWindow(image, "Rendered Micro QR Code", true);
ShowImages.showWindow(output, "Rendered Micro QR Code", true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2022, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package boofcv.abst.fiducial;

import boofcv.BoofTesting;
import boofcv.abst.distort.FDistort;
import boofcv.alg.fiducial.microqr.MicroQrCode;
import boofcv.alg.fiducial.microqr.MicroQrCodeEncoder;
import boofcv.alg.fiducial.microqr.MicroQrCodeGenerator;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.factory.fiducial.FactoryFiducial;
import boofcv.misc.BoofMiscOps;
import boofcv.struct.image.GrayU8;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2)
@Measurement(iterations = 3)
@State(Scope.Benchmark)
@Fork(value = 1)
public class BenchmarkMicroQrCodeDetector {

List<GrayU8> images = new ArrayList<>();

MicroQrCodeDetector<GrayU8> detector = FactoryFiducial.microqr(null, GrayU8.class);

/**
* Generate a set of synthetic images with two markers in it to test against
*/
@Setup public void setup() {
var rand = new Random(BoofTesting.BASE_SEED);
MicroQrCode qr1 = new MicroQrCodeEncoder().addAutomatic("1").fixate();
MicroQrCode qr2 = new MicroQrCodeEncoder().addAutomatic("ALPHA NUMERIC 123").fixate();

GrayU8 image1 = MicroQrCodeGenerator.renderImage(5, 2, qr1);
GrayU8 image2 = MicroQrCodeGenerator.renderImage(5, 2, qr2);

var fullImage = new GrayU8(image1.width + image2.width + 50, image1.width + image2.width + 100);
GImageMiscOps.fillUniform(fullImage, rand, 50, 150);

GImageMiscOps.copy(0, 0, 10, 15, image1.width, image1.height, image1, fullImage);
GImageMiscOps.copy(0, 0, 20 + image1.width, 50, image2.width, image2.height, image2, fullImage);

images.add(fullImage);

// manually checked that all the distorted images have markers inside the image
for (int i = 0; i < 4; i++) {
GrayU8 distorted = fullImage.createSameShape();
new FDistort(fullImage, distorted).affine(
1.0, rand.nextGaussian()*0.01,
rand.nextGaussian()*0.01, 1.0,
rand.nextGaussian()*0.5, rand.nextGaussian()*0.5).apply();
images.add(distorted);
}
}

@Benchmark public void qrcode() {
for (int imageIdx = 0; imageIdx < images.size(); imageIdx++) {
detector.process(images.get(imageIdx));
// sanity check to make sure everything is running as expected
BoofMiscOps.checkEq(2, detector.getDetections().size());
}
}

public static void main( String[] args ) throws RunnerException {
Options opt = new OptionsBuilder()
.include(BenchmarkMicroQrCodeDetector.class.getSimpleName())
.warmupTime(TimeValue.seconds(1))
.measurementTime(TimeValue.seconds(1))
.build();

new Runner(opt).run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2022, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package boofcv.abst.fiducial;

import boofcv.BoofTesting;
import boofcv.abst.distort.FDistort;
import boofcv.alg.fiducial.qrcode.QrCode;
import boofcv.alg.fiducial.qrcode.QrCodeEncoder;
import boofcv.alg.fiducial.qrcode.QrCodeGeneratorImage;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.factory.fiducial.FactoryFiducial;
import boofcv.misc.BoofMiscOps;
import boofcv.struct.image.GrayU8;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2)
@Measurement(iterations = 3)
@State(Scope.Benchmark)
@Fork(value = 1)
public class BenchmarkQrCodeDetector {

List<GrayU8> images = new ArrayList<>();

QrCodeDetector<GrayU8> detector = FactoryFiducial.qrcode(null, GrayU8.class);

/**
* Generate a set of synthetic images with two markers in it to test against
*/
@Setup public void setup() {
var rand = new Random(BoofTesting.BASE_SEED);
QrCode qr1 = new QrCodeEncoder().addAutomatic("small").fixate();
QrCode qr2 = new QrCodeEncoder().addAutomatic("Much Larger Than the Oth9er!!#").fixate();

GrayU8 image1 = new QrCodeGeneratorImage(5).render(qr1).getGray();
GrayU8 image2 = new QrCodeGeneratorImage(5).render(qr2).getGray();

var fullImage = new GrayU8(image1.width + image2.width + 50, image1.width + image2.width + 100);
GImageMiscOps.fillUniform(fullImage, rand, 50, 150);

GImageMiscOps.copy(0, 0, 10, 15, image1.width, image1.height, image1, fullImage);
GImageMiscOps.copy(0, 0, 20 + image1.width, 50, image2.width, image2.height, image2, fullImage);

images.add(fullImage);

// manually checked that all the distorted images have markers inside the image
for (int i = 0; i < 4; i++) {
GrayU8 distorted = fullImage.createSameShape();
new FDistort(fullImage, distorted).affine(
1.0 + rand.nextGaussian()*0.1, rand.nextGaussian()*0.01,
rand.nextGaussian()*0.01, 1.0 + rand.nextGaussian()*0.1,
rand.nextGaussian()*0.5, rand.nextGaussian()*0.5).apply();
images.add(distorted);
}
}

@Benchmark public void qrcode() {
for (int imageIdx = 0; imageIdx < images.size(); imageIdx++) {
detector.process(images.get(imageIdx));
// sanity check to make sure everything is running as expected
BoofMiscOps.checkEq(2, detector.getDetections().size());
}
}

public static void main( String[] args ) throws RunnerException {
Options opt = new OptionsBuilder()
.include(BenchmarkQrCodeDetector.class.getSimpleName())
.warmupTime(TimeValue.seconds(1))
.measurementTime(TimeValue.seconds(1))
.build();

new Runner(opt).run();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@

package boofcv.alg.fiducial.microqr;

import boofcv.alg.drawing.FiducialImageEngine;
import boofcv.alg.fiducial.qrcode.PackedBits32;
import boofcv.alg.fiducial.qrcode.QrCodeCodeWordLocations;
import boofcv.alg.fiducial.qrcode.QrGeneratorBase;
import boofcv.struct.image.GrayU8;
import georegression.struct.point.Point2D_I32;

/**
Expand All @@ -29,6 +31,16 @@
* @author Peter Abeles
*/
public class MicroQrCodeGenerator extends QrGeneratorBase<MicroQrCodeGenerator> {

/** Convenience function for rendering images */
public static GrayU8 renderImage( int pixelPerModule, int border, MicroQrCode qr ) {
int numModules = MicroQrCode.totalModules(qr.version);
var render = new FiducialImageEngine();
render.configure(pixelPerModule*border, numModules*pixelPerModule);
new MicroQrCodeGenerator().setMarkerWidth(numModules*pixelPerModule).setRender(render).render(qr);
return render.getGray();
}

public MicroQrCodeGenerator render( MicroQrCode qr ) {
numModules = MicroQrCode.totalModules(qr.version);
moduleWidth = markerWidth/numModules;
Expand Down

0 comments on commit 22eb1fa

Please sign in to comment.