1
0
Fork 0
SVEN/QR/src/SvenQR.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öß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 über den Rand und die Ausmaße
BorderInformation info;
// Gemessene Größ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öß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öße auslesen
int width = img.getWidth();
int height = img.getHeight();
//
// = BILD ENTSÄTTIGEN UND KONTRAST SPREIZEN =
//
// Jedes Pixel durchgehen und in Graustufen umwandeln,
// danach den Kontrast spreizen, sodass nur noch schwarze
// und weiß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ö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ü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 ü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ß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ä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öß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öß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ä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öß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 =
//
// Äuß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;
}
}