From ac90ca398ae175e03f7096f9de1e08ad93f581bb Mon Sep 17 00:00:00 2001 From: Peter Abeles Date: Sun, 23 Jan 2022 21:35:32 -0800 Subject: [PATCH] BoofMiscOps - Added md5sum function QrCode - Renamed "raw" to "binary" since it is more commonly referred to as binary data --- ...Data.java => ExampleQrCodeBinaryData.java} | 15 +++++------ .../alg/fiducial/qrcode/EciEncoding.java | 4 +++ .../fiducial/qrcode/QrCodeCodecBitsUtils.java | 2 +- .../factory/fiducial/ConfigMicroQrCode.java | 2 +- .../boofcv/factory/fiducial/ConfigQrCode.java | 2 +- .../qrcode/TestQrCodeCodecBitsUtils.java | 10 ++++---- .../qrcode/TestQrCodeDecoderBits.java | 6 ++--- .../main/java/boofcv/misc/BoofMiscOps.java | 25 +++++++++++++++++-- .../java/boofcv/misc/TestBoofMiscOps.java | 2 +- 9 files changed, 47 insertions(+), 21 deletions(-) rename examples/src/main/java/boofcv/examples/fiducial/{ExampleQrCodeRawData.java => ExampleQrCodeBinaryData.java} (88%) diff --git a/examples/src/main/java/boofcv/examples/fiducial/ExampleQrCodeRawData.java b/examples/src/main/java/boofcv/examples/fiducial/ExampleQrCodeBinaryData.java similarity index 88% rename from examples/src/main/java/boofcv/examples/fiducial/ExampleQrCodeRawData.java rename to examples/src/main/java/boofcv/examples/fiducial/ExampleQrCodeBinaryData.java index 207639ddcb..9a6a4d66b8 100644 --- a/examples/src/main/java/boofcv/examples/fiducial/ExampleQrCodeRawData.java +++ b/examples/src/main/java/boofcv/examples/fiducial/ExampleQrCodeBinaryData.java @@ -19,6 +19,7 @@ package boofcv.examples.fiducial; import boofcv.abst.fiducial.QrCodeDetector; +import boofcv.alg.fiducial.qrcode.EciEncoding; import boofcv.alg.fiducial.qrcode.QrCode; import boofcv.alg.fiducial.qrcode.QrCodeEncoder; import boofcv.alg.fiducial.qrcode.QrCodeGeneratorImage; @@ -33,11 +34,11 @@ * When dealing with binary data embedded in a QR code things do get more complicated. You will need to convert * the string message into a byte array. By default, it's assumed that BYTE data is a text string and it will * encode it into a string. In general the conversion to a string will not modify the data but it's not defined - * how illegal characters are encoded. To be safe you can force the encoding to be "raw". + * how illegal characters are encoded. To be safe you can force the encoding to be "binary". * * @author Peter Abeles */ -public class ExampleQrCodeRawData { +public class ExampleQrCodeBinaryData { public static void main( String[] args ) throws UnsupportedEncodingException { // Let's generate some random data. In the real world this could be a zip file or similar byte[] originalData = new byte[500]; @@ -51,9 +52,9 @@ public static void main( String[] args ) throws UnsupportedEncodingException { // Let's detect and then decode the image var config = new ConfigQrCode(); - // If you force the encoding to "raw" then you turn off auto encoding you know the bit values will not + // If you force the encoding to "binary" then you turn off auto encoding you know the bit values will not // be modified. It's undefined how illegal values are handled in different encodings, but often still work. - config.forceEncoding = "raw"; + config.forceEncoding = EciEncoding.BINARY; QrCodeDetector detector = FactoryFiducial.qrcode(config, GrayU8.class); detector.process(gray); @@ -62,10 +63,10 @@ public static void main( String[] args ) throws UnsupportedEncodingException { if (qr.mode != QrCode.Mode.BYTE) continue; - // Convert the message from a String to byte data. This only works if the 'raw' encoding is used + // Convert the message from a String to byte data. This only works if the 'binary' encoding is used byte[] data; - if (qr.byteEncoding.equals("raw")) { - data = BoofMiscOps.stringRawToByteArray(qr.message); + if (qr.byteEncoding.equals(EciEncoding.BINARY)) { + data = BoofMiscOps.castStringToByteArray(qr.message); } else { // If it thought the byte data was UTF-8 you need to decode with UTF-8 because a single character // can be multiple bytes. diff --git a/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/EciEncoding.java b/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/EciEncoding.java index 6414dfe4cf..2e88eeef01 100644 --- a/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/EciEncoding.java +++ b/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/EciEncoding.java @@ -24,6 +24,10 @@ * @author Peter Abeles */ public class EciEncoding { + // BINARY is specific to BoofCV and is used to indicate that there should be no encoding done + public static final String BINARY = "binary"; + + // Standard QR Code string encodings public static final String UTF8 = "UTF8"; public static final String ISO8859_1 = "ISO8859_1"; public static final String JIS = "JIS"; diff --git a/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/QrCodeCodecBitsUtils.java b/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/QrCodeCodecBitsUtils.java index 8dfab66027..32ccdf967a 100644 --- a/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/QrCodeCodecBitsUtils.java +++ b/main/boofcv-recognition/src/main/java/boofcv/alg/fiducial/qrcode/QrCodeCodecBitsUtils.java @@ -191,7 +191,7 @@ public int decodeByte( PackedBits8 data, int bitLocation, int lengthBits ) { selectedByteEncoding = selectByteEncoding(rawdata); try { - if (selectedByteEncoding.equalsIgnoreCase("raw")) { + if (selectedByteEncoding.equalsIgnoreCase(EciEncoding.BINARY)) { // Handle raw mode where there is no encoding which could change the character's value. // This is what the QR code specifications says you should use, but most ignore it workString.ensureCapacity(workString.length() + rawdata.length); diff --git a/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigMicroQrCode.java b/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigMicroQrCode.java index 48f39b8639..bc8cc31ba2 100644 --- a/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigMicroQrCode.java +++ b/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigMicroQrCode.java @@ -44,7 +44,7 @@ public class ConfigMicroQrCode implements Configuration { /** * If not null, then when decoding BYTE mode data it will always use this encoding. This can be desirable * if the automatic encoding detection is making a mistake or if you know the data is binary. For binary - * data you should set this to "raw". + * data you should set this to {@link EciEncoding#BINARY}. */ public @Nullable String forceEncoding = null; diff --git a/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigQrCode.java b/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigQrCode.java index 7754ef916a..c0f0f5f9ae 100644 --- a/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigQrCode.java +++ b/main/boofcv-recognition/src/main/java/boofcv/factory/fiducial/ConfigQrCode.java @@ -50,7 +50,7 @@ public class ConfigQrCode implements Configuration { /** * If not null, then when decoding BYTE mode data it will always use this encoding. This can be desirable * if the automatic encoding detection is making a mistake or if you know the data is binary. For binary - * data you should set this to "raw". + * data you should set this to {@link EciEncoding#BINARY}. */ public @Nullable String forceEncoding = null; diff --git a/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeCodecBitsUtils.java b/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeCodecBitsUtils.java index cb7f0104c1..312ccbde11 100644 --- a/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeCodecBitsUtils.java +++ b/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeCodecBitsUtils.java @@ -48,18 +48,18 @@ public class TestQrCodeCodecBitsUtils extends BoofStandardJUnit { } /** Tell it to encode a raw byte array and see if it does it correctly. No hints */ - @Test void rawBytesAutoEncoding() { - checkRawBytes(255, null); + @Test void binaryAutoEncoding() { + checkBinaryBytes(255, null); } /** Tell it that it should use raw bytes */ - @Test void rawBytesHint() { + @Test void binaryBytesHint() { // reduce the number of values so that UTF-8 is still an option, and it will modify the byte // values unless you force it into raw mode - checkRawBytes(150, "raw"); + checkBinaryBytes(150, EciEncoding.BINARY); } - void checkRawBytes( int length, @Nullable String forceEncoding ) { + void checkBinaryBytes( int length, @Nullable String forceEncoding ) { // encode fewer values so that it could possibly be UTF-8 without the hint byte[] data = new byte[length]; for (int i = 0; i < data.length; i++) { diff --git a/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeDecoderBits.java b/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeDecoderBits.java index f0b825af3f..79c36dfd5b 100644 --- a/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeDecoderBits.java +++ b/main/boofcv-recognition/src/test/java/boofcv/alg/fiducial/qrcode/TestQrCodeDecoderBits.java @@ -27,7 +27,7 @@ public class TestQrCodeDecoderBits extends BoofStandardJUnit { @Test public void applyErrorCorrection() { QrCode qr = new QrCodeEncoder().addNumeric("923492348985").fixate(); - var alg = new QrCodeDecoderBits(EciEncoding.UTF8, "raw"); + var alg = new QrCodeDecoderBits(EciEncoding.UTF8, EciEncoding.BINARY); byte[] original = new byte[qr.rawbits.length]; System.arraycopy(qr.rawbits, 0, original, 0, original.length); @@ -71,7 +71,7 @@ public class TestQrCodeDecoderBits extends BoofStandardJUnit { @Test public void checkPaddingBytes() { QrCode qr = new QrCodeEncoder().addNumeric("923492348985").fixate(); - var alg = new QrCodeDecoderBits(EciEncoding.UTF8, "raw"); + var alg = new QrCodeDecoderBits(EciEncoding.UTF8, EciEncoding.BINARY); qr.corrected = new byte[50]; @@ -102,7 +102,7 @@ public class TestQrCodeDecoderBits extends BoofStandardJUnit { // ECI Assignment number bits.append(0b00001001, 8, false); - var alg = new QrCodeDecoderBits(EciEncoding.UTF8, "raw"); + var alg = new QrCodeDecoderBits(EciEncoding.UTF8, EciEncoding.BINARY); int newBit = alg.decodeEci(bits, 0); assertEquals("ISO8859_7", alg.encodingEci); diff --git a/main/boofcv-types/src/main/java/boofcv/misc/BoofMiscOps.java b/main/boofcv-types/src/main/java/boofcv/misc/BoofMiscOps.java index eb3c944a4c..58f9ec7f4d 100644 --- a/main/boofcv-types/src/main/java/boofcv/misc/BoofMiscOps.java +++ b/main/boofcv-types/src/main/java/boofcv/misc/BoofMiscOps.java @@ -35,6 +35,7 @@ import java.io.*; import java.lang.reflect.Method; +import java.security.MessageDigest; import java.time.Instant; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; @@ -45,7 +46,7 @@ * * @author Peter Abeles */ -@SuppressWarnings("rawtypes") +@SuppressWarnings({"rawtypes", "ForLoopReplaceableByForEach"}) public class BoofMiscOps { /** @@ -1019,11 +1020,31 @@ public static int getJavaVersion() { * @param message String with raw bytes encoded inside of it. * @return byte array conversion of the string. */ - public static byte[] stringRawToByteArray( String message ) { + public static byte[] castStringToByteArray( String message ) { byte[] data = new byte[message.length()]; for (int i = 0; i < message.length(); i++) { data[i] = (byte)message.charAt(i); } return data; } + + /** + * Computes MD5SUM of byte data as a string + */ + public static String md5sum( byte[] data ) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + return byteArrayToHex(md.digest(data)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static String byteArrayToHex(byte[] array) { + var sb = new StringBuilder(array.length * 2); + for (int i = 0; i < array.length; i++) { + sb.append(String.format("%02X", array[i])); + } + return sb.toString(); + } } diff --git a/main/boofcv-types/src/test/java/boofcv/misc/TestBoofMiscOps.java b/main/boofcv-types/src/test/java/boofcv/misc/TestBoofMiscOps.java index 11a290474b..be2c8632cb 100644 --- a/main/boofcv-types/src/test/java/boofcv/misc/TestBoofMiscOps.java +++ b/main/boofcv-types/src/test/java/boofcv/misc/TestBoofMiscOps.java @@ -252,7 +252,7 @@ void containsDuplicates() { builder.append((char)i); } - byte[] found = BoofMiscOps.stringRawToByteArray(builder.toString()); + byte[] found = BoofMiscOps.castStringToByteArray(builder.toString()); for (int i = 0; i < N; i++) { assertEquals(i & 0xFF, found[i] & 0xFF); }