1 /* 2 * Copyright 2013 ZXing authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.zxing.aztec.detector; 18 19 import com.google.zxing.NotFoundException; 20 import com.google.zxing.aztec.AztecDetectorResult; 21 import com.google.zxing.aztec.decoder.Decoder; 22 import com.google.zxing.aztec.detector.Detector.Point; 23 import com.google.zxing.aztec.encoder.AztecCode; 24 import com.google.zxing.aztec.encoder.Encoder; 25 import com.google.zxing.common.BitMatrix; 26 import com.google.zxing.common.DecoderResult; 27 import org.junit.Assert; 28 import org.junit.Test; 29 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.List; 34 import java.util.Random; 35 import java.util.TreeSet; 36 37 /** 38 * Tests for the Detector 39 * 40 * @author Frank Yellin 41 */ 42 public final class DetectorTest extends Assert { 43 44 @Test testErrorInParameterLocatorZeroZero()45 public void testErrorInParameterLocatorZeroZero() throws Exception { 46 // Layers=1, CodeWords=1. So the parameter info and its Reed-Solomon info 47 // will be completely zero! 48 testErrorInParameterLocator("X"); 49 } 50 51 @Test testErrorInParameterLocatorCompact()52 public void testErrorInParameterLocatorCompact() throws Exception { 53 testErrorInParameterLocator("This is an example Aztec symbol for Wikipedia."); 54 } 55 56 @Test testErrorInParameterLocatorNotCompact()57 public void testErrorInParameterLocatorNotCompact() throws Exception { 58 String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz"; 59 testErrorInParameterLocator(alphabet + alphabet + alphabet); 60 } 61 62 // Test that we can tolerate errors in the parameter locator bits testErrorInParameterLocator(String data)63 private static void testErrorInParameterLocator(String data) throws Exception { 64 AztecCode aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS); 65 Random random = new Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic 66 int layers = aztec.getLayers(); 67 boolean compact = aztec.isCompact(); 68 List<Point> orientationPoints = getOrientationPoints(aztec); 69 for (boolean isMirror : new boolean[] { false, true }) { 70 for (BitMatrix matrix : getRotations(aztec.getMatrix())) { 71 // Systematically try every possible 1- and 2-bit error. 72 for (int error1 = 0; error1 < orientationPoints.size(); error1++) { 73 for (int error2 = error1; error2 < orientationPoints.size(); error2++) { 74 BitMatrix copy = isMirror ? transpose(matrix) : clone(matrix); 75 copy.flip(orientationPoints.get(error1).getX(), orientationPoints.get(error1).getY()); 76 if (error2 > error1) { 77 // if error2 == error1, we only test a single error 78 copy.flip(orientationPoints.get(error2).getX(), orientationPoints.get(error2).getY()); 79 } 80 // The detector doesn't seem to work when matrix bits are only 1x1. So magnify. 81 AztecDetectorResult r = new Detector(makeLarger(copy, 3)).detect(isMirror); 82 assertNotNull(r); 83 assertEquals(r.getNbLayers(), layers); 84 assertEquals(r.isCompact(), compact); 85 DecoderResult res = new Decoder().decode(r); 86 assertEquals(data, res.getText()); 87 } 88 } 89 // Try a few random three-bit errors; 90 for (int i = 0; i < 5; i++) { 91 BitMatrix copy = clone(matrix); 92 Collection<Integer> errors = new TreeSet<>(); 93 while (errors.size() < 3) { 94 // Quick and dirty way of getting three distinct integers between 1 and n. 95 errors.add(random.nextInt(orientationPoints.size())); 96 } 97 for (int error : errors) { 98 copy.flip(orientationPoints.get(error).getX(), orientationPoints.get(error).getY()); 99 } 100 try { 101 new Detector(makeLarger(copy, 3)).detect(false); 102 fail("Should not reach here"); 103 } catch (NotFoundException expected) { 104 // continue 105 } 106 } 107 } 108 } 109 } 110 111 // Zooms a bit matrix so that each bit is factor x factor makeLarger(BitMatrix input, int factor)112 private static BitMatrix makeLarger(BitMatrix input, int factor) { 113 int width = input.getWidth(); 114 BitMatrix output = new BitMatrix(width * factor); 115 for (int inputY = 0; inputY < width; inputY++) { 116 for (int inputX = 0; inputX < width; inputX++) { 117 if (input.get(inputX, inputY)) { 118 output.setRegion(inputX * factor, inputY * factor, factor, factor); 119 } 120 } 121 } 122 return output; 123 } 124 125 // Returns a list of the four rotations of the BitMatrix. getRotations(BitMatrix matrix0)126 private static Iterable<BitMatrix> getRotations(BitMatrix matrix0) { 127 BitMatrix matrix90 = rotateRight(matrix0); 128 BitMatrix matrix180 = rotateRight(matrix90); 129 BitMatrix matrix270 = rotateRight(matrix180); 130 return Arrays.asList(matrix0, matrix90, matrix180, matrix270); 131 } 132 133 // Rotates a square BitMatrix to the right by 90 degrees rotateRight(BitMatrix input)134 private static BitMatrix rotateRight(BitMatrix input) { 135 int width = input.getWidth(); 136 BitMatrix result = new BitMatrix(width); 137 for (int x = 0; x < width; x++) { 138 for (int y = 0; y < width; y++) { 139 if (input.get(x,y)) { 140 result.set(y, width - x - 1); 141 } 142 } 143 } 144 return result; 145 } 146 147 // Returns the transpose of a bit matrix, which is equivalent to rotating the 148 // matrix to the right, and then flipping it left-to-right transpose(BitMatrix input)149 private static BitMatrix transpose(BitMatrix input) { 150 int width = input.getWidth(); 151 BitMatrix result = new BitMatrix(width); 152 for (int x = 0; x < width; x++) { 153 for (int y = 0; y < width; y++) { 154 if (input.get(x, y)) { 155 result.set(y, x); 156 } 157 } 158 } 159 return result; 160 } 161 clone(BitMatrix input)162 private static BitMatrix clone(BitMatrix input) { 163 int width = input.getWidth(); 164 BitMatrix result = new BitMatrix(width); 165 for (int x = 0; x < width; x++) { 166 for (int y = 0; y < width; y++) { 167 if (input.get(x,y)) { 168 result.set(x,y); 169 } 170 } 171 } 172 return result; 173 } 174 getOrientationPoints(AztecCode code)175 private static List<Point> getOrientationPoints(AztecCode code) { 176 int center = code.getMatrix().getWidth() / 2; 177 int offset = code.isCompact() ? 5 : 7; 178 List<Point> result = new ArrayList<>(); 179 for (int xSign = -1; xSign <= 1; xSign += 2) { 180 for (int ySign = -1; ySign <= 1; ySign += 2) { 181 result.add(new Point(center + xSign * offset, center + ySign * offset)); 182 result.add(new Point(center + xSign * (offset - 1), center + ySign * offset)); 183 result.add(new Point(center + xSign * offset, center + ySign * (offset - 1))); 184 } 185 } 186 return result; 187 } 188 189 } 190