// Updates: 2002.06.26 import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.io.*; import java.awt.image.*; import java.awt.geom.*; import java.util.*; /** * Propose des méthodes statiques pour manipuler des images. */ public class ImgTools { /** * Crée une BufferedImage à partir d'une Image. */ public static BufferedImage getBufferedImage(Image img) { int w = img.getWidth(null); int h = img.getHeight(null); BufferedImage bImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = bImage.createGraphics(); g2.drawImage(img, 0, 0, null); return bImage; } /** * Crée une BufferedImage à partir d'un tableau de booléens. */ public static BufferedImage getBufferedImage(boolean[][] img) { int w = img.length; int h = img[0].length; BufferedImage bImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if (img[i][j]) bImage.setRGB(i, j, 0xff000000); else bImage.setRGB(i, j, 0xffffffff); } } return bImage; } /** * Crée une BufferedImage à partir d'un tableau d'entiers, chacun étant * l'indice de la couleur à appliquer. */ public static BufferedImage getBufferedImage(int[][] img, Color[] color) { int w = img.length; int h = img[0].length; BufferedImage bImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int index = img[i][j]; if ((index >= 0) && (index < color.length)) { Color c = color[index]; bImage.setRGB(i, j, c.getRGB()); } } } return bImage; } /** * Copie une BufferedImage */ public static BufferedImage copy(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); BufferedImage bImage2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = bImage2.createGraphics(); g2.drawImage(bImage, 0, 0, null); return bImage2; } /** * Agrandi une image de telle manière à ce qu'elle devienne carrée. Les * nouveaux pixels sont blancs. */ public static BufferedImage squared(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); int max = (w > h ? w : h); BufferedImage bImage2 = new BufferedImage(max, max, BufferedImage.TYPE_INT_ARGB); for (int i = 0; i < max; i++) { for (int j = 0; j < max; j++) { bImage2.setRGB(i, j, 0xffffffff); } } Graphics2D g2 = bImage2.createGraphics(); g2.drawImage(bImage, (max-w)/2, (max-h)/2, null); return bImage2; } /** * Agrandi une image de telle manière à ce qu'elle devienne carrée, et * que son côté soit égal à la digonale de l'image. Les nouveaux pixels * sont blancs. Cette méthode est utilisée avant une rotation, afin de ne * pas perdre des pixels. */ public static BufferedImage diagSized(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); int diag = (int)(Math.sqrt(w*w+h*h)); BufferedImage bImage2 = new BufferedImage(diag, diag, BufferedImage.TYPE_INT_ARGB); for (int i = 0; i < diag; i++) { for (int j = 0; j < diag; j++) { bImage2.setRGB(i, j, 0xffffffff); } } Graphics2D g2 = bImage2.createGraphics(); g2.drawImage(bImage, (diag-w)/2, (diag-h)/2, null); return bImage2; } /** * Crée une image en niveaux de gris. */ public static BufferedImage grayScale(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); BufferedImage bImage2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int p = bImage.getRGB(i, j); int a = (((p >> 16) & 0xff) + ((p >> 8) & 0xff) + (p & 0xff)) / 3; bImage2.setRGB(i, j, (0xff << 24) | (a << 16) | (a << 8) | a); } } return bImage2; } /** * Crée une image binaire, en utilisant le seuil spécifié. */ public static BufferedImage binary(BufferedImage bImage, int threshold) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); BufferedImage bImage2 = grayScale(bImage); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if ((bImage2.getRGB(i, j) & 0xff) > threshold) bImage2.setRGB(i, j, 0xffffffff); else bImage2.setRGB(i, j, 0xff000000); } } return bImage2; } /** * Crée une image binaire, en calculant le seuil automatiquement à partir * de l'histogramme. */ public static BufferedImage binary(BufferedImage bImage) { final int n = 51; // size of the filter, must be uneven int space = (n-1)/2; int[] histo = histogram(bImage); int[] hBig = new int[histo.length+2*space]; for (int i = 0; i < histo.length; i++) { hBig[i+space] = histo[i]; } int[] histo2 = new int[histo.length]; for (int i = 0; i < histo.length; i++) { int sum = 0; for (int j = 0; j < n; j++) { sum += hBig[i+j]; } histo2[i] = sum/n; } int[] min = minimums(histo2); int threshold = 0; for (int i = 0; i < min.length; i++) { if (min[i] > threshold) threshold = min[i]; } return binary(bImage, threshold); } /** * Crée une image binaire sous forme d'un tableau de booléens, en * utilisant le seuil spécifié. */ public static boolean[][] getBoolean(BufferedImage bImage, int threshold) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); BufferedImage bImage2 = grayScale(bImage); boolean[][] image = new boolean[w][h]; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if ((bImage2.getRGB(i, j) & 0xff) <= threshold) image[i][j] = true; } } return image; } /** * Crée une image binaire sous forme d'un tableau de booléens, en * calculant le seuil automatiquement à partir de l'histogramme. */ public static boolean[][] getBoolean(BufferedImage bImage) { return getBoolean(binary(bImage), 127); } /** * Remplace les pixels de valeur 0 (fond de l'image) par des pixels * blancs. */ public static BufferedImage whiteBackground(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); BufferedImage bImage2 = copy(bImage); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if (bImage2.getRGB(i, j) == 0) bImage2.setRGB(i, j, 0xffffffff); } } return bImage2; } /** * Retourne le plus petit rectangle pouvant contenir tous les pixels * différents du pixel blanc. Attention: Un pixel de fond n'est pas un * pixel blanc ! */ public static Rectangle getBounds(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); int iMin = 1000000; int jMin = 1000000; int iMax = -1; int jMax = -1; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if (bImage.getRGB(i, j) != 0xffffffff) { if (i < iMin) iMin = i; if (i > iMax) iMax = i; if (j < jMin) jMin = j; if (j > jMax) jMax = j; } } } if (iMax < 0) return new Rectangle(0, 0, 0, 0); // no points... return new Rectangle(iMin, jMin, iMax-iMin, jMax-jMin); } /** * Tourne l'image autour de son centre. L'angle est spécifié en degrés. * Le booléen whiteBackground spécifie si il faut que les nouveaux pixels * générés par la rotation soient coloriés en blanc ou pas. */ public static BufferedImage rotate(BufferedImage bImage, int angle, boolean whiteBackground) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(angle), w/2, h/2); AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR); BufferedImage bImage2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); op.filter(bImage, bImage2); if (whiteBackground) bImage2 = ImgTools.whiteBackground(bImage2); return bImage2; } /** * Compte le nombre de pixels différents du pixel blanc. Attention: Un * pixel de fond n'est pas un pixel blanc ! */ public static int surface(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); int sum = 0; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { if (bImage.getRGB(i, j) != 0xffffffff) sum++; } } return sum; } /** * Calcule l'histogramme d'une image, sous forme d'un tableau de 256 * entiers. Une image couleur est d'abord transformée en niveaux de gris. */ public static int[] histogram(BufferedImage bImage) { int w = bImage.getWidth(null); int h = bImage.getHeight(null); BufferedImage bImage2 = grayScale(bImage); int[] histo = new int[256]; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int color = bImage2.getRGB(i, j) & 0xff; histo[color]++; } } return histo; } /** * Retourne un tableau contenant les minimums d'une fonction. */ private static int[] minimums(int[] function) { final int up = 1; final int down = -1; ArrayList values = new ArrayList(); int x = 0; int way = 0; for (int i = 0; i < function.length; i++) { if (function[i] > x) { if (way == down) values.add(new Integer(i-1)); x = function[i]; way = up; } if (function[i] < x) { x = function[i]; way = down; } } int[] vector = new int[values.size()]; for (int i = 0; i < vector.length; i++) { vector[i] = ((Integer)values.get(i)).intValue(); } return vector; } }