www.pudn.com > QRcode.rar > QRCodeDecoder.java, change:2005-08-21,size:20099b
/*
* 作成日: 2004/09/12
*
* TODO この生成されたファイルのテンプレートを変更するには次を参照。
* ウィンドウ > 設定 > Java > コード・スタイル > コード・テンプレート
*/
package jp.sourceforge.qrcode.codec;
import java.util.Vector;
import jp.sourceforge.qrcode.codec.data.QRCodeSymbol;
import jp.sourceforge.qrcode.codec.ecc.BCH15_5;
import jp.sourceforge.qrcode.codec.ecc.ReedSolomon;
import jp.sourceforge.qrcode.codec.exception.AlignmentPatternEdgeNotFoundException;
import jp.sourceforge.qrcode.codec.exception.DecodingFailedException;
import jp.sourceforge.qrcode.codec.exception.FinderPatternNotFoundException;
import jp.sourceforge.qrcode.codec.exception.IllegalDataBlockException;
import jp.sourceforge.qrcode.codec.exception.SymbolNotFoundException;
import jp.sourceforge.qrcode.codec.exception.VersionInformationException;
import jp.sourceforge.qrcode.codec.reader.QRCodeDataBlockReader;
import jp.sourceforge.qrcode.codec.reader.QRCodeImageReader;
import jp.sourceforge.qrcode.codec.util.DebugCanvas;
/**
* @author Owner
*
* TODO この生成された型コメントのテンプレートを変更するには次を参照。
* ウィンドウ > 設定 > Java > コード・スタイル > コード・テンプレート
*/
public class QRCodeDecoder {
//QRCodeImageReader reader;
int internalScale;
QRCodeSymbol symbol;
DebugCanvas canvas;
//デコーダ本体
public QRCodeDecoder() {
internalScale = 2;
canvas = DebugCanvas.getCanvas();
}
public String decode(int[][] image) throws DecodingFailedException{
canvas.println("Decoding started.");
try {
symbol = getQRCodeSymbol(image);
} catch (SymbolNotFoundException e) {
e.printStackTrace();
throw new DecodingFailedException();
}
canvas.println("Created QRCode symbol.");
//int ratio = canvas.getWidth() / symbol.getWidth();
//canvas.drawMatrix(symbol , ratio);
canvas.println("Reading symbol.");
boolean[] formatInformation = getFormatInformation(symbol);
symbol.setFormatInformation(formatInformation);
canvas.println("Version: " + symbol.getVersionReference());
String maskPattern = Integer.toString(symbol.getMaskPatternReferer() ,2);
int length = maskPattern.length();
for (int i = 0; i < 3 - length; i++)
maskPattern = "0" + maskPattern;
canvas.println("Mask pattern: " + maskPattern);
canvas.println("Unmasking.");
unmask(symbol);
//canvas.drawMatrix(symbol, 4);
int[] blocks = getBlocks(symbol);
canvas.println("Correcting data errors.");
int[] dataBlocks = getCorrectedDataBlocks(blocks);
String decodedString = "";
try {
decodedString = getDecodedString(dataBlocks, symbol.getVersion());
} catch (IllegalDataBlockException e) {
e.printStackTrace();
throw new DecodingFailedException();
}
// return new QRCodeContent(decodedString);
canvas.println("Decoding finished.");
return decodedString;
}
boolean[][] processImage(int[][] image) {
imageToGrayScale(image);
boolean[][] bitmap = grayScaleToBitmap(image);
//boolean[][] bitmapEx = extendBitmap(bitmap, internalScale);
return bitmap;
}
void imageToGrayScale(int[][] image) {
for (int y = 0; y < image[0].length; y++) {
for (int x = 0; x < image.length; x++) {
int r = image[x][y] >> 16 & 0xFF;
int g = image[x][y] >> 8 & 0xFF;
int b = image[x][y] & 0xFF;
int m = (r * 30 + g * 59 + b * 11) / 100;
image[x][y] = m;
}
}
}
boolean[][] grayScaleToBitmap_(int[][] grayScale) {
int[][] middle = findAreaMiddle(grayScale);
int[] minmax = findMinMax(grayScale);
boolean[][] bitmap = new boolean[grayScale.length][grayScale[0].length];
int halftone = (minmax[0] + minmax[1]) / 2;
for (int y = 0; y < grayScale[0].length; y++) {
for (int x = 0; x < grayScale.length; x++) {
bitmap[x][y] = (grayScale[x][y] < halftone) ? true : false;
}
}
return bitmap;
}
boolean[][] grayScaleToBitmap(int[][] grayScale) {
int[][] middle = findAreaMiddle(grayScale);
int sqrtNumArea = middle.length;
int areaWidth = grayScale.length / sqrtNumArea;
int areaHeight = grayScale[0].length / sqrtNumArea;
boolean[][] bitmap = new boolean[grayScale.length][grayScale[0].length];
for (int ay = 0; ay < sqrtNumArea; ay++) {
for (int ax = 0; ax < sqrtNumArea; ax++) {
for (int dy = 0; dy < areaHeight; dy++) {
for (int dx = 0; dx < areaWidth; dx++) {
bitmap[areaWidth * ax + dx][areaHeight * ay + dy] = (grayScale[areaWidth * ax + dx][areaHeight * ay + dy] < middle[ax][ay]) ? true : false;
}
}
}
}
return bitmap;
}
int[] findMinMax(int[][] image) {
int tempMin = Integer.MAX_VALUE;
int tempMax = Integer.MIN_VALUE;
for (int y = 0; y < image[0].length; y++) {
for (int x = 0; x < image.length; x++) {
if (image[x][y] < tempMin)
tempMin = image[x][y];
else if (image[x][y] > tempMax)
tempMax = image[x][y];
}
}
return new int[] {tempMin, tempMax};
}
int[][] findAreaMiddle(int[][] image) {
final int numSqrtArea = 4;
//4x4のエリアごとの明るさの中間値((min + max) / 2)を出す
int areaWidth = image.length / numSqrtArea;
int areaHeight = image[0].length / numSqrtArea;
int[][][] minmax = new int[numSqrtArea][numSqrtArea][2];
for (int ay = 0; ay < numSqrtArea; ay++) {
for (int ax = 0; ax < numSqrtArea; ax++) {
minmax[ax][ay][0] = 0xFF;
for (int dy = 0; dy < areaHeight; dy++) {
for (int dx = 0; dx < areaWidth; dx++) {
int target = image[areaWidth * ax + dx][areaHeight * ay + dy];
if (target < minmax[ax][ay][0]) minmax[ax][ay][0] = target;
if (target > minmax[ax][ay][1]) minmax[ax][ay][1] = target;
}
}
//minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
}
}
int[][] middle = new int[numSqrtArea][numSqrtArea];
for (int ay = 0; ay < numSqrtArea; ay++) {
for (int ax = 0; ax < numSqrtArea; ax++) {
middle[ax][ay] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
//System.out.print(middle[ax][ay] + ",");
}
//System.out.println("");
}
//System.out.println("");
return middle;
}
boolean[][] extendBitmap(boolean[][] bitmap, int scale) {
boolean[][] bitmap2x = new boolean[bitmap.length * 2][bitmap[0].length * 2];
for (int y = 0; y < bitmap[0].length; y++) {
for (int x = 0; x < bitmap.length; x++) {
if (bitmap[x][y] == true) {
for (int sx = 0; sx < scale; sx++)
for (int sy = 0; sy < scale; sy++)
bitmap2x[x * scale + sx][y * scale + sy] = true;
}
}
}
return bitmap2x;
}
QRCodeSymbol getQRCodeSymbol(int[][] image) throws SymbolNotFoundException {
//canvas.println("Creating binary matrix.");
//BinaryMatrix binaryImage = new BinaryMatrix(imageData);
//canvas.println("Drawing matrix.");
//canvas.drawMatrix(binaryImage);
//canvas.println("Reading matrix");
canvas.println("Creating bitmap.");
boolean[][] bitmap = processImage(image);
QRCodeImageReader reader = new QRCodeImageReader();
QRCodeSymbol symbol = null;
try {
symbol = reader.getQRCodeSymbol(bitmap, internalScale);
} catch (FinderPatternNotFoundException e) {
throw new SymbolNotFoundException();
} catch (VersionInformationException e2) {
throw new SymbolNotFoundException();
} catch (AlignmentPatternEdgeNotFoundException e3) {
throw new SymbolNotFoundException();
}
return symbol;
}
boolean[] getFormatInformation(QRCodeSymbol qRCodeMatrix) {
boolean[] modules = new boolean[15];
//マトリックスから形式情報部分取り出し
for (int i = 0; i <= 5; i++)
modules[i] = qRCodeMatrix.getElement(8, i);
modules[6] = qRCodeMatrix.getElement(8, 7);
modules[7] = qRCodeMatrix.getElement(8, 8);
modules[8] = qRCodeMatrix.getElement(7, 8);
for (int i = 9; i <= 14; i++)
modules[i] = qRCodeMatrix.getElement(14 - i, 8);
//XOR演算でマスク処理
int maskPattern = 0x5412;
for (int i = 0; i <= 14; i++) {
boolean xorBit = false;
if (((maskPattern >>> i) & 1) == 1)
xorBit = true;
else
xorBit = false;
if (modules[i] == xorBit) //ビットシフトした後の一桁目を見る
modules[i] = false;
else
modules[i] = true;
}
//int a[] = {0,1,2,4,8,3,6,12,11,5,10,7,14,15,13,9,1};
//System.out.println("debug");
//printBit("formatInfo", modules);
//エラー訂正
BCH15_5 corrector = new BCH15_5(modules);
boolean[] output = corrector.correct();
int numError = corrector.getNumCorrectedError();
if (numError > 0)
canvas.println(String.valueOf(numError) + " format errors corrected.");
boolean[] formatInformation = new boolean[5];
for (int i = 0; i < 5; i++)
formatInformation[i] = output[10 + i];
return formatInformation;
}
void unmask(QRCodeSymbol symbol) {
int maskPatternReferer = symbol.getMaskPatternReferer();
//マスクパターン生成
boolean[][] maskPattern = generateMaskPattern(symbol);
int size = symbol.getWidth();
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
if (maskPattern[x][y] == true) {
symbol.reverseElement(x, y);
}
}
}
}
boolean[][] generateMaskPattern(QRCodeSymbol symbol) {
int maskPatternReferer = symbol.getMaskPatternReferer();
//マスクパターン生成
int width = symbol.getWidth();
int height = symbol.getHeight();
boolean[][] maskPattern = new boolean[width][height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (symbol.isInFunctionPattern(x, y))
continue;
switch (maskPatternReferer) {
case 0: // 000
if ((x + y) % 2 == 0)
maskPattern[x][y] = true;
break;
case 1: // 001
if (y % 2 == 0)
maskPattern[x][y] = true;
break;
case 2: // 010
if (x % 3 == 0)
maskPattern[x][y] = true;
break;
case 3: // 011
if ((x + y) % 3 == 0)
maskPattern[x][y] = true;
break;
case 4: // 100
if ((x / 3 + y / 2) % 2 == 0)
maskPattern[x][y] = true;
break;
case 5: // 101
if ((x * y) % 2 + (x * y) % 3 == 0)
maskPattern[x][y] = true;
break;
case 6: // 110
if (((x * y) % 2 + (x * y) % 3) % 2 == 0)
maskPattern[x][y] = true;
break;
case 7: // 111
if (((x * y) % 3 + (x + y) % 2) % 2 == 0)
maskPattern[x][y] = true;
break;
}
}
}
return maskPattern;
}
int[] getBlocks(QRCodeSymbol symbol) {
int width = symbol.getWidth();
//System.out.println("SymbolWidth:" + Integer.toString(symbol.getWidth()));
//System.out.println("SymbolHeight:" + Integer.toString(symbol.getHeight()));
int height = symbol.getHeight();
int x = width - 1;
int y = height - 1;
Vector codeBits = new Vector();
Vector codeWords = new Vector();
int tempWord = 0;
int figure = 7;
int isNearFinish = 0;
final boolean READ_UP = true;
final boolean READ_DOWN = false;
boolean direction = READ_UP;
do {
//canvas.drawPoint(new Point(x * 4 +8 , y * 4 + 47), Color.RED);
codeBits.addElement(new Boolean(symbol.getElement(x, y)));
//System.out.println(Integer.toString(codeBits.size()));
//canvas.drawPoint(new Point(x*3 + 120,y*3 + 190), Color.RED);
//int ratio = canvas.getWidth() / symbol.getWidth();
//int offsetX = (canvas.getWidth() - symbol.getWidth() * ratio) / 2;
//int offsetY = (canvas.getHeight() - symbol.getHeight() * ratio) / 2;
//canvas.drawPoint(new Point(offsetX + x * ratio + 3, offsetY + y * ratio + 3), 0xFF0000);
if (symbol.getElement(x, y) == true) {
tempWord += 1 << figure;
}
//System.out.println(new Point(x, y).toString() + " " + symbol.getElement(x, y));
figure--;
if (figure == -1) {
codeWords.addElement(new Integer(tempWord));
//System.out.print(codeWords.size() + ": ");
//System.out.println(tempWord);
figure = 7;
tempWord = 0;
}
//次に読むモジュールを決定する
do {
if (direction == READ_UP) {
if ((x + isNearFinish) % 2 == 0) //二列のうち右側なら
x--; //左側
else {
if (y > 0) { //上に進める
x++;
y--;
}
else { //進めない
x--; //方向転換し
if (x == 6){
x--;
isNearFinish=1; // 縦のタイミングパターンを通過することによって判定を変える
}
direction = READ_DOWN;
}
}
}
else {
if ((x + isNearFinish) % 2 == 0) //二列のうち左側なら
x--;
else {
if (y < height - 1) {
x++;
y++;
}
else {
x--;
if (x == 6){
x--;
isNearFinish=1;
}
direction = READ_UP;
}
}
}
} while (symbol.isInFunctionPattern(x, y));
} while (x != -1);
int[] gotWords = new int[codeWords.size()];
for (int i = 0; i < codeWords.size(); i++) {
Integer temp = (Integer)codeWords.elementAt(i);
gotWords[i] = temp.intValue();
}
return gotWords;
}
int[] getCorrectedDataBlocks(int[] blocks) {
int numErrors = 0;
//System.out.println(":");
//System.out.println("blockLength: " + blocks.length);
int version = symbol.getVersion();
//System.out.println("Version: " + version);
int errorCollectionLevel = symbol.getErrorCollectionLevel();
//System.out.println("errorCollectionLevel:" + errorCollectionLevel);
int dataCapacity = symbol.getDataCapacity();
int[] dataBlocks = new int[dataCapacity];
//System.out.println("dataCapacity: " + dataCapacity);
int numErrorCollectionCode = symbol.getNumErrorCollectionCode();
//System.out.println("numErrorCollectionCode:" + numErrorCollectionCode);
int numRSBlocks = symbol.getNumRSBlocks();
int eccPerRSBlock = numErrorCollectionCode / numRSBlocks;
//System.out.println("numRSBlocks: " + numRSBlocks);
if (numRSBlocks == 1) {
//[TODO]エラー訂正処理
// for (int k = 0; k < blocks.length; k++) {
// System.out.print(String.valueOf(blocks[k]) + ",");
// }
// System.out.println("");
// System.out.println("numWords="+String.valueOf(blocks.length));
ReedSolomon corrector = new ReedSolomon(blocks);
corrector.correct();
numErrors += corrector.getNumCorrectedErrors();
if (numErrors > 0)
canvas.println(String.valueOf(numErrors) + " data errors corrected.");
else
canvas.println("No errors found.");
return blocks;
}
else { //RSブロックが2つ以上のため、データブロック並び替えあり
int numLongerRSBlocks = dataCapacity % numRSBlocks;
if (numLongerRSBlocks == 0) { //RSブロックは1種類
int lengthRSBlock = dataCapacity / numRSBlocks;
int[][] RSBlocks = new int[numRSBlocks][lengthRSBlock];
//RSブロックを得る
for (int i = 0; i < numRSBlocks; i++) {
//System.out.println("i = " + i);
//for (int j = 0; j < lengthRSBlock - numErrorCollectionCode / numRSBlocks ; j++) {
for (int j = 0; j < lengthRSBlock; j++) {
//System.out.println("j = " + j);
//try {
RSBlocks[i][j] = blocks[j * numRSBlocks + i];
//} catch (ArrayIndexOutOfBoundsException e) {}
}
//[TODO]エラー訂正処理
// for (int k = 0; k < RSBlocks[i].length; k++) {
// System.out.print(String.valueOf(RSBlocks[i][k]) + ",");
// }
//System.out.println("");
//System.out.println("numWords="+String.valueOf(RSBlocks[i].length));
ReedSolomon corrector = new ReedSolomon(RSBlocks[i]);
corrector.correct();
numErrors += corrector.getNumCorrectedErrors();
}
//データ部分のみ抜き出す
int p = 0;
for (int i = 0; i < numRSBlocks; i++) {
for (int j = 0; j < lengthRSBlock - eccPerRSBlock; j++) {
dataBlocks[p++] = RSBlocks[i][j];
}
}
}
else { //RSブロックは2種類
int lengthShorterRSBlock = dataCapacity / numRSBlocks;
//System.out.println("lengthShorterRSBlock : " + lengthShorterRSBlock);
int lengthLongerRSBlock = dataCapacity / numRSBlocks + 1;
//System.out.println("lengthLongerRSBlock: " + lengthLongerRSBlock);
int numShorterRSBlocks = numRSBlocks - numLongerRSBlocks;
//System.out.println("numShorterRSBlocks: " + numShorterRSBlocks);
//System.out.println("numLongerRSBlocks: " + numLongerRSBlocks);
int[][] shorterRSBlocks = new int[numShorterRSBlocks][lengthShorterRSBlock];
int[][] longerRSBlocks = new int[numLongerRSBlocks][lengthLongerRSBlock];
for (int i = 0; i < numRSBlocks; i++) {
//System.out.println("i = " + i);
if (i < numShorterRSBlocks) {
//短い方のRSブロックを得る
//for (int j = 0; j < lengthShorterRSBlock - numErrorCollectionCode / numRSBlocks ; j++) {
int mod = 0;
for (int j = 0; j < lengthShorterRSBlock; j++) {
//System.out.println(" j = " + j);
if (j == lengthShorterRSBlock - eccPerRSBlock) mod = numLongerRSBlocks;
//System.out.print(String.valueOf(j * numRSBlocks + i + mod) + ",");
shorterRSBlocks[i][j] = blocks[j * numRSBlocks + i + mod];
}
//System.out.println("");
//[TODO]エラー訂正処理
// for (int k = 0; k < shorterRSBlocks[i].length; k++) {
// System.out.print(String.valueOf(shorterRSBlocks[i][k]) + ",");
// }
//System.out.println("");
//System.out.println("numWords="+String.valueOf(shorterRSBlocks[i].length));
ReedSolomon corrector = new ReedSolomon(shorterRSBlocks[i]);
corrector.correct();
numErrors += corrector.getNumCorrectedErrors();
}
else {
//System.out.println("Debug" + String.valueOf(numShorterRSBlocks));
//長い方のRSブロックを得る
//for (int j = 0; j < lengthLongerRSBlock - numErrorCollectionCode / numRSBlocks ; j++) {
int mod = 0;
for (int j = 0; j < lengthLongerRSBlock; j++) {
//System.out.println(" j = " + j);
//try {
if (j == lengthShorterRSBlock - eccPerRSBlock) mod = numShorterRSBlocks;
// System.out.print("," + String.valueOf(j * numRSBlocks + i - mod));
longerRSBlocks[i - numShorterRSBlocks][j] = blocks[j * numRSBlocks + i - mod];
//} catch (Exception e) {e.getMessage();}
}
//System.out.println("debug2");
//[TODO]エラー訂正処理
// for (int k = 0; k < longerRSBlocks[i - numShorterRSBlocks].length; k++) {
// System.out.print(String.valueOf(longerRSBlocks[i - numShorterRSBlocks][k]) + ",");
// }
//System.out.println("");
//System.out.println("numWords="+String.valueOf(longerRSBlocks[i - numShorterRSBlocks].length));
ReedSolomon corrector = new ReedSolomon(longerRSBlocks[i - numShorterRSBlocks]);
corrector.correct();
numErrors += corrector.getNumCorrectedErrors();
}
}
int p = 0;
for (int i = 0; i < numRSBlocks; i++) {
if (i < numShorterRSBlocks) {
for (int j = 0; j < lengthShorterRSBlock - eccPerRSBlock; j++) {
dataBlocks[p++] = shorterRSBlocks[i][j];
}
}
else {
for (int j = 0; j < lengthLongerRSBlock - eccPerRSBlock; j++) {
dataBlocks[p++] = longerRSBlocks[i - numShorterRSBlocks][j];
}
}
}
}
if (numErrors > 0)
canvas.println(String.valueOf(numErrors) + " data errors corrected.");
return dataBlocks;
}
}
String getDecodedString(int[] blocks, int version) throws IllegalDataBlockException {
//canvas.println("Reading data.");
String dataString = null;
QRCodeDataBlockReader reader = new QRCodeDataBlockReader(blocks, version);
try {
dataString = reader.getDataString();
} catch (ArrayIndexOutOfBoundsException e) {
//canvas.println("ERROR: Data block error");
throw new IllegalDataBlockException();
}
return dataString;
}
public DebugCanvas getCanvas() {
return canvas;
}
public void setCanvas(DebugCanvas canvas) {
this.canvas = canvas;
}
}