485 lines
14 KiB
Java
485 lines
14 KiB
Java
|
import java.awt.*;
|
|||
|
import java.awt.image.*;
|
|||
|
import java.awt.print.PageFormat;
|
|||
|
import java.awt.print.Printable;
|
|||
|
import java.awt.print.PrinterException;
|
|||
|
import java.awt.print.PrinterJob;
|
|||
|
import java.io.*;
|
|||
|
import javax.imageio.ImageIO;
|
|||
|
|
|||
|
public class SvenQR {
|
|||
|
// Anzahl der Rechtecke horizontal
|
|||
|
public static final int AMNT_RECT_X = 4;
|
|||
|
// Anzahl der Rechtecke vertikal
|
|||
|
public static final int AMNT_RECT_Y = 3;
|
|||
|
// Rahmendicke in Pixeln
|
|||
|
public static final int BORDER_WIDTH = 8;
|
|||
|
// Rechteckgr<67><72>e in Pixeln
|
|||
|
public static final int RECT_WIDTH = 40;
|
|||
|
|
|||
|
// Liest das angegebene Bild ein und wertet den QR-Code aus.
|
|||
|
public static int ReadQR(String fileName) throws IOException {
|
|||
|
// Quellbild
|
|||
|
BufferedImage img = ImageIO.read(new File(fileName));
|
|||
|
// Informationen <20>ber den Rand und die Ausma<6D>e
|
|||
|
BorderInformation info;
|
|||
|
// Gemessene Gr<47><72>e des QR-Codes
|
|||
|
Pair measuredSize;
|
|||
|
// Wert des QR-Codes
|
|||
|
int value;
|
|||
|
|
|||
|
img = _IN_PrepareImage(img);
|
|||
|
|
|||
|
info = _IN_GetBorderWidth(img);
|
|||
|
|
|||
|
Pair imageSize = new Pair((int) img.getWidth(), (int) img.getHeight());
|
|||
|
measuredSize = _IN_GetMeasuredSize(img, info, imageSize);
|
|||
|
|
|||
|
// Durchschnittsgr<67><72>e der Rechtecke
|
|||
|
int avgSquareWidth = (int) ((measuredSize.x - 4f * info.BorderWidth) / AMNT_RECT_X);
|
|||
|
int avgSquareHeight = (int) ((measuredSize.y - 4f * info.BorderWidth) / AMNT_RECT_Y);
|
|||
|
Pair avgSquareSize = new Pair(avgSquareWidth, avgSquareHeight);
|
|||
|
|
|||
|
value = _IN_HeuristicDetermination(img, info, avgSquareSize);
|
|||
|
|
|||
|
return value;
|
|||
|
}
|
|||
|
|
|||
|
public static BufferedImage GenerateQR(int value) {
|
|||
|
boolean[][] bits;
|
|||
|
BufferedImage img;
|
|||
|
|
|||
|
// Konvertiere die Zahl in eine Bit-Tabelle, die der QR-Code-Darstellung
|
|||
|
// entspricht
|
|||
|
bits = _OUT_GetBits(value);
|
|||
|
|
|||
|
// Generiere Bild
|
|||
|
img = _OUT_DrawImage(bits);
|
|||
|
|
|||
|
return img;
|
|||
|
}
|
|||
|
|
|||
|
public static BufferedImage _IN_PrepareImage(BufferedImage img) {
|
|||
|
// Bildgr<67><72>e auslesen
|
|||
|
int width = img.getWidth();
|
|||
|
int height = img.getHeight();
|
|||
|
|
|||
|
//
|
|||
|
// = BILD ENTS<54>TTIGEN UND KONTRAST SPREIZEN =
|
|||
|
//
|
|||
|
// Jedes Pixel durchgehen und in Graustufen umwandeln,
|
|||
|
// danach den Kontrast spreizen, sodass nur noch schwarze
|
|||
|
// und wei<65>e Pixel im Bild sind, anders kann der Algorithmus
|
|||
|
// nicht arbeiten.
|
|||
|
//
|
|||
|
|
|||
|
for (int y = 0; y < height; y++) {
|
|||
|
for (int x = 0; x < width; x++) {
|
|||
|
|
|||
|
// Aktuelles Pixel auslesen
|
|||
|
int value = img.getRGB(x, y);
|
|||
|
Color c1 = new Color(value);
|
|||
|
|
|||
|
// Graustufe berechnen
|
|||
|
int grau = (c1.getBlue() + c1.getRed() + c1.getGreen()) / 3;
|
|||
|
|
|||
|
// Kontrast spreizen
|
|||
|
if (grau >= 127) {
|
|||
|
grau = 255;
|
|||
|
} else {
|
|||
|
grau = 0;
|
|||
|
}
|
|||
|
|
|||
|
// Pixel ersetzen
|
|||
|
Color c = new Color(grau, grau, grau);
|
|||
|
img.setRGB(x, y, c.getRGB());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*try {
|
|||
|
File fx = new File("X:/ents.png"); // Speicherort
|
|||
|
ImageIO.write(img, "png", fx); // Bildformat
|
|||
|
} catch (IOException e) {
|
|||
|
System.out.println("Error: " + e);
|
|||
|
}*/
|
|||
|
|
|||
|
//
|
|||
|
// = ALLEINSTEHENDE PIXEL IM BILD ENTFERNEN =
|
|||
|
//
|
|||
|
// Wenn das Bild fleckig ist, entstehen einzelne Pixel, die
|
|||
|
// den Algorithmus zum auslesen st<73>ren k<>nnen, also werden
|
|||
|
// diese entfernt.
|
|||
|
//
|
|||
|
|
|||
|
for (int x = 0; x < width; x++) {
|
|||
|
for (int y = 0; y < height; y++) {
|
|||
|
boolean weissAussendrum = true;
|
|||
|
|
|||
|
// Rechts
|
|||
|
if (!(x < width - 1 && img.getRGB(x + 1, y) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
// Links
|
|||
|
if (!(x > 0 && img.getRGB(x - 1, y) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
// Drunter
|
|||
|
if (!(y < height - 1 && img.getRGB(x, y + 1) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
// Dr<44>ber
|
|||
|
if (!(y > 0 && img.getRGB(x, y - 1) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
|
|||
|
// Links oben
|
|||
|
if (!(x > 0 && y > 0 && img.getRGB(x - 1, y - 1) == - 1))
|
|||
|
weissAussendrum = false;
|
|||
|
// Links unten
|
|||
|
if (!(x > 0 && y < height - 1 && img.getRGB(x - 1, y + 1) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
// Rechts oben
|
|||
|
if (!(x < width - 1 && y > 0 && img.getRGB(x + 1, y - 1) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
// Rechts unten
|
|||
|
if (!(x < width - 1 && y < height - 1 && img.getRGB(x + 1, y + 1) == -1))
|
|||
|
weissAussendrum = false;
|
|||
|
|
|||
|
if (weissAussendrum)
|
|||
|
img.setRGB(x, y, -1 );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*try {
|
|||
|
File fx = new File("X:/prep2.png"); // Speicherort
|
|||
|
ImageIO.write(img, "png", fx); // Bildformat
|
|||
|
} catch (IOException e) {
|
|||
|
System.out.println("Error: " + e);
|
|||
|
}*/
|
|||
|
|
|||
|
return img;
|
|||
|
}
|
|||
|
|
|||
|
private static BorderInformation _IN_GetBorderWidth(BufferedImage img) {
|
|||
|
Color pixel;
|
|||
|
|
|||
|
// Informationen <20>ber den Rahmen des QR-Codes
|
|||
|
BorderInformation info = null;
|
|||
|
|
|||
|
// Breite des schwarzen Rahmens und Orientierungspunkte
|
|||
|
float borderWidth = -1f;
|
|||
|
Pair firstBlackPixel = new Pair(-1, -1);
|
|||
|
Pair firstWhitePixel;
|
|||
|
|
|||
|
//
|
|||
|
// = SUCHE DAS ERSTE SCHWARZE PIXEL IN DER LINKEN OBEREN ECKE =
|
|||
|
//
|
|||
|
|
|||
|
// Gehe diagonal durch das Bild auf der Suche nach schwarzen Pixeln
|
|||
|
while (!_IN_CompareColors(img.getRGB(firstBlackPixel.x + 1, firstBlackPixel.y + 1), Color.BLACK)) {
|
|||
|
firstBlackPixel.x++;
|
|||
|
firstBlackPixel.y++;
|
|||
|
}
|
|||
|
|
|||
|
firstBlackPixel.x++;
|
|||
|
firstBlackPixel.y++;
|
|||
|
|
|||
|
// Aufgrund von Ungenauigkeiten beim Drucken, gehen wir drei Pixel in den Rand rein
|
|||
|
firstBlackPixel.x += 2;
|
|||
|
|
|||
|
System.out.println("Suche schwarzes Pixel.");
|
|||
|
System.out.println("X: " + firstBlackPixel.x + " Y: " + firstBlackPixel.y + "\n");
|
|||
|
|
|||
|
|
|||
|
// Gehe so weit wie m<>glich nach oben
|
|||
|
while (firstBlackPixel.y > 0
|
|||
|
&& _IN_CompareColors(img.getRGB(firstBlackPixel.x, firstBlackPixel.y - 1), Color.BLACK))
|
|||
|
firstBlackPixel.y--;
|
|||
|
|
|||
|
System.out.println("Gehe so weit wie m<>glich nach oben");
|
|||
|
System.out.println("X: " + firstBlackPixel.x + " Y: " + firstBlackPixel.y + "\n");
|
|||
|
|
|||
|
// Gehe so weit wie m<>glich nach links
|
|||
|
while (firstBlackPixel.x > 0
|
|||
|
&& _IN_CompareColors(img.getRGB(firstBlackPixel.x - 1, firstBlackPixel.y), Color.BLACK))
|
|||
|
firstBlackPixel.x--;
|
|||
|
|
|||
|
System.out.println("Gehe so weit wie m<>glich nach links");
|
|||
|
System.out.println("X: " + firstBlackPixel.x + " Y: " + firstBlackPixel.y + "\n");
|
|||
|
|
|||
|
//
|
|||
|
// = SUCHE DAS ERSTE WEISSE PIXEL VOM INNEREN RAHMEN =
|
|||
|
//
|
|||
|
|
|||
|
firstWhitePixel = new Pair(firstBlackPixel);
|
|||
|
|
|||
|
// Gehe diagonal vom ersten schwarzen Pixel durch das Bild
|
|||
|
while (!_IN_CompareColors(img.getRGB(firstWhitePixel.x + 1, firstWhitePixel.y + 1), Color.WHITE)) {
|
|||
|
firstWhitePixel.x++;
|
|||
|
firstWhitePixel.y++;
|
|||
|
}
|
|||
|
|
|||
|
firstWhitePixel.x++;
|
|||
|
firstWhitePixel.y++;
|
|||
|
|
|||
|
System.out.println("Suche wei<65>es Pixel");
|
|||
|
System.out.println("X: " + firstWhitePixel.x + " Y: " + firstWhitePixel.y + "\n");
|
|||
|
|
|||
|
// Gehe so weit wie m<>glich nach oben
|
|||
|
while (_IN_CompareColors(img.getRGB(firstWhitePixel.x, firstWhitePixel.y - 1), Color.WHITE))
|
|||
|
firstWhitePixel.y--;
|
|||
|
|
|||
|
System.out.println("Gehe soweit wie m<>glich nach oben");
|
|||
|
System.out.println("X: " + firstWhitePixel.x + " Y: " + firstWhitePixel.y + "\n");
|
|||
|
|
|||
|
// Gehe so weit wie m<>glich nach links
|
|||
|
while (_IN_CompareColors(img.getRGB(firstWhitePixel.x - 1, firstWhitePixel.y), Color.WHITE))
|
|||
|
firstWhitePixel.x--;
|
|||
|
|
|||
|
System.out.println("Gehe soweit wie m<>glich nach links");
|
|||
|
System.out.println("X: " + firstWhitePixel.x + " Y: " + firstWhitePixel.y + "\n");
|
|||
|
|
|||
|
// Differenzen, also Rahmenst<73>rke ausrechnen
|
|||
|
int deltaX = firstWhitePixel.x - firstBlackPixel.x;
|
|||
|
int deltaY = firstWhitePixel.y - firstBlackPixel.y;
|
|||
|
|
|||
|
// Durchschnitt ermitteln
|
|||
|
borderWidth = (deltaX + deltaY) / 2f;
|
|||
|
|
|||
|
System.out.println("Border width: " + borderWidth);
|
|||
|
|
|||
|
info = new BorderInformation(borderWidth, firstBlackPixel);
|
|||
|
return info;
|
|||
|
}
|
|||
|
|
|||
|
private static Pair _IN_GetMeasuredSize(BufferedImage img, BorderInformation borderInfo, Pair imageSize) {
|
|||
|
Pair measuredSize;
|
|||
|
Pair upperRightBlackPixel, lowerLeftBlackPixel;
|
|||
|
Color pixel;
|
|||
|
|
|||
|
//
|
|||
|
// = MESSE DIE BREITE DES QR-CODES =
|
|||
|
//
|
|||
|
|
|||
|
upperRightBlackPixel = new Pair(borderInfo.firstBlackPixel);
|
|||
|
// Weil das Bild nicht immer gerade ist, messen wir in der Mitte des
|
|||
|
// schwarzen Rahmens
|
|||
|
upperRightBlackPixel.y += borderInfo.BorderWidth / 2f;
|
|||
|
|
|||
|
while (upperRightBlackPixel.x < imageSize.x - 1
|
|||
|
&& !_IN_CompareColors(img.getRGB(upperRightBlackPixel.x + 1, upperRightBlackPixel.y), Color.WHITE))
|
|||
|
upperRightBlackPixel.x++;
|
|||
|
|
|||
|
//
|
|||
|
// = MESSE DIE H<>HE DES QR-CODES =
|
|||
|
//
|
|||
|
|
|||
|
lowerLeftBlackPixel = new Pair(borderInfo.firstBlackPixel);
|
|||
|
// Immer mittig messen, kann bei arg schiefen Bildern jedoch auch wieder
|
|||
|
// zu fehlerhaften Ergebnissen f<>hren
|
|||
|
// In solch einem Fall k<>nnen wir nichts tun :/
|
|||
|
lowerLeftBlackPixel.x += borderInfo.BorderWidth / 2f;
|
|||
|
|
|||
|
while (lowerLeftBlackPixel.y < imageSize.y - 1
|
|||
|
&& !_IN_CompareColors(img.getRGB(upperRightBlackPixel.x, upperRightBlackPixel.y + 1), Color.WHITE))
|
|||
|
lowerLeftBlackPixel.y++;
|
|||
|
|
|||
|
// Errechne die Gr<47><72>e des QR-Codes aus den Differenzen der Punkte
|
|||
|
measuredSize = new Pair(upperRightBlackPixel.x - borderInfo.firstBlackPixel.x,
|
|||
|
lowerLeftBlackPixel.y - borderInfo.firstBlackPixel.y);
|
|||
|
measuredSize.x++;
|
|||
|
measuredSize.y++;
|
|||
|
|
|||
|
System.out.println("Gemessene Gr<47><72>e: " + measuredSize.x + " x " + measuredSize.y);
|
|||
|
|
|||
|
return measuredSize;
|
|||
|
}
|
|||
|
|
|||
|
private static int _IN_HeuristicDetermination(BufferedImage img, BorderInformation borderInfo, Pair avgSquareSize) {
|
|||
|
boolean[][] bits = new boolean[AMNT_RECT_X][AMNT_RECT_Y];
|
|||
|
float halfWidth = avgSquareSize.x / 2f, halfHeight = avgSquareSize.y / 2f;
|
|||
|
int c_value;
|
|||
|
|
|||
|
for (int x = 0; x < AMNT_RECT_X; x++) {
|
|||
|
for (int y = 0; y < AMNT_RECT_Y; y++) {
|
|||
|
|
|||
|
int _x = (int) (2 * borderInfo.BorderWidth);
|
|||
|
_x += (int) (x * avgSquareSize.x);
|
|||
|
_x += (int) halfWidth;
|
|||
|
_x += borderInfo.firstBlackPixel.x;
|
|||
|
|
|||
|
int _y = (int) (2 * borderInfo.BorderWidth);
|
|||
|
_y += (int) (y * avgSquareSize.y);
|
|||
|
_y += (int) halfHeight;
|
|||
|
_y += borderInfo.firstBlackPixel.y;
|
|||
|
|
|||
|
c_value = img.getRGB(_x, _y);
|
|||
|
System.out.println("X: " + _x + " Y: " + _y + " V: " + (_IN_CompareColors(c_value, Color.WHITE) ? false : true));
|
|||
|
|
|||
|
if (_IN_CompareColors(c_value, Color.WHITE))
|
|||
|
bits[x][y] = false;
|
|||
|
else if (_IN_CompareColors(c_value, Color.BLACK))
|
|||
|
bits[x][y] = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int value = 0;
|
|||
|
|
|||
|
if (bits[0][0])
|
|||
|
value += 1;
|
|||
|
if (bits[1][0])
|
|||
|
value += 2;
|
|||
|
if (bits[2][0])
|
|||
|
value += 4;
|
|||
|
if (bits[3][0])
|
|||
|
value += 8;
|
|||
|
|
|||
|
if (bits[0][1])
|
|||
|
value += 16;
|
|||
|
if (bits[1][1])
|
|||
|
value += 32;
|
|||
|
if (bits[2][1])
|
|||
|
value += 64;
|
|||
|
if (bits[3][1])
|
|||
|
value += 128;
|
|||
|
|
|||
|
if (bits[0][2])
|
|||
|
value += 256;
|
|||
|
if (bits[1][2])
|
|||
|
value += 512;
|
|||
|
if (bits[2][2])
|
|||
|
value += 1024;
|
|||
|
if (bits[3][2])
|
|||
|
value += 2048;
|
|||
|
|
|||
|
return value;
|
|||
|
}
|
|||
|
|
|||
|
private static boolean _IN_CompareColors(int rgb, Color template) {
|
|||
|
Color c = new Color(rgb);
|
|||
|
if (c.getRed() != template.getRed())
|
|||
|
return false;
|
|||
|
if (c.getGreen() != template.getGreen())
|
|||
|
return false;
|
|||
|
if (c.getBlue() != template.getBlue())
|
|||
|
return false;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
private static boolean[][] _OUT_GetBits(int value) {
|
|||
|
// Die Bits in QR-Code-Darstellung
|
|||
|
boolean[][] bits = new boolean[AMNT_RECT_X][AMNT_RECT_Y];
|
|||
|
// Die Bits als Bin<69>rstring
|
|||
|
String bitsAsString = Integer.toBinaryString(value);
|
|||
|
// Anzahl an f<>hrenden Nullen f<>r 12-Bit:
|
|||
|
int amntLeadingZeros = 12 - bitsAsString.length();
|
|||
|
|
|||
|
// Stelle f<>hrende Nullen voran
|
|||
|
for (int i = 0; i < amntLeadingZeros; i++)
|
|||
|
bitsAsString = "0" + bitsAsString;
|
|||
|
|
|||
|
bits[0][0] = bitsAsString.charAt(11) == '0' ? false : true;
|
|||
|
bits[1][0] = bitsAsString.charAt(10) == '0' ? false : true;
|
|||
|
bits[2][0] = bitsAsString.charAt(9) == '0' ? false : true;
|
|||
|
bits[3][0] = bitsAsString.charAt(8) == '0' ? false : true;
|
|||
|
bits[0][1] = bitsAsString.charAt(7) == '0' ? false : true;
|
|||
|
bits[1][1] = bitsAsString.charAt(6) == '0' ? false : true;
|
|||
|
bits[2][1] = bitsAsString.charAt(5) == '0' ? false : true;
|
|||
|
bits[3][1] = bitsAsString.charAt(4) == '0' ? false : true;
|
|||
|
bits[0][2] = bitsAsString.charAt(3) == '0' ? false : true;
|
|||
|
bits[1][2] = bitsAsString.charAt(2) == '0' ? false : true;
|
|||
|
bits[2][2] = bitsAsString.charAt(1) == '0' ? false : true;
|
|||
|
bits[3][2] = bitsAsString.charAt(0) == '0' ? false : true;
|
|||
|
|
|||
|
return bits;
|
|||
|
}
|
|||
|
|
|||
|
private static BufferedImage _OUT_DrawImage(boolean[][] bits) {
|
|||
|
// Bildgr<67><72>e
|
|||
|
Pair imageSize;
|
|||
|
// Bild
|
|||
|
BufferedImage img;
|
|||
|
// Grafikobjekt zum zeichnen
|
|||
|
Graphics2D g;
|
|||
|
|
|||
|
// Bildbreite und -h<>he berechnen
|
|||
|
// Bei 72 dpi entspricht 1mm 3px, wir nehmen 2px
|
|||
|
imageSize = new Pair(BORDER_WIDTH * 4 + AMNT_RECT_X * RECT_WIDTH, BORDER_WIDTH * 4 + AMNT_RECT_Y * RECT_WIDTH);
|
|||
|
|
|||
|
// Bild erstellen und Grafikobjekt erstellen
|
|||
|
img = new BufferedImage(imageSize.x, imageSize.y, BufferedImage.TYPE_INT_ARGB);
|
|||
|
g = img.createGraphics();
|
|||
|
|
|||
|
//
|
|||
|
// = QR-CODE-ZEICHEN =
|
|||
|
//
|
|||
|
|
|||
|
// <20>u<EFBFBD>eren Rahmen zeichnen
|
|||
|
g.setColor(Color.BLACK);
|
|||
|
g.fillRect(0, 0, imageSize.x, imageSize.y);
|
|||
|
|
|||
|
// Inneren Rahmen zeichnen
|
|||
|
g.setColor(Color.WHITE);
|
|||
|
g.fillRect(BORDER_WIDTH, BORDER_WIDTH, imageSize.x - 2 * BORDER_WIDTH, imageSize.y - 2 * BORDER_WIDTH);
|
|||
|
|
|||
|
g.setColor(Color.BLACK);
|
|||
|
|
|||
|
// Rechtecke zeichnen
|
|||
|
for (int x = 0; x < AMNT_RECT_X; x++) {
|
|||
|
for (int y = 0; y < AMNT_RECT_Y; y++) {
|
|||
|
int _x = BORDER_WIDTH * 2 + x * RECT_WIDTH;
|
|||
|
int _y = BORDER_WIDTH * 2 + y * RECT_WIDTH;
|
|||
|
|
|||
|
if (!bits[x][y])
|
|||
|
continue;
|
|||
|
|
|||
|
g.fillRect(_x, _y, RECT_WIDTH, RECT_WIDTH);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Rechte untere Ecke als Orientierungspunkt entfernen
|
|||
|
g.setColor(Color.WHITE);
|
|||
|
g.fillRect(imageSize.x - BORDER_WIDTH, imageSize.y - BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH);
|
|||
|
|
|||
|
return img;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|