■ サンプル
* 使用する画像は、ペイントで適当に白黒でテストデータ画像を作って実行した
Main.java
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
try {
JFrame frame = new JFrame("Thinning Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ThinningPanel panel = new ThinningPanel("C:\\temp\\sample.png");
frame.add(panel);
frame.pack();
frame.setSize(1200, 500);
frame.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
ThinningPanel.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.MediaTracker;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ThinningPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final static int UPPER_LEFT = 2;
private final static int LOWER_RIGHT = 6;
private final static int UPPER_RIGHT = 0;
private final static int LOWER_LEFT = 4;
private final static byte BLACK = 1;
private final static byte WHITE = 0;
private final static int THRESHOLD = 128;
private BufferedImage srcImage;
private boolean hasChanged;
private byte[][] newPixels;
private byte[][] oldPixels;
private int imageWidth;
private int imageHeight;
public ThinningPanel(String imagePath) throws IOException, InterruptedException {
init(imagePath);
}
private void init(String imagePath) throws IOException, InterruptedException {
File pathToFile = new File(imagePath);
this.srcImage = ImageIO.read(pathToFile);
MediaTracker mediaTracker = new MediaTracker(this);
mediaTracker.addImage(this.srcImage, 0);
mediaTracker.waitForAll();
this.imageWidth = this.srcImage.getWidth();
this.imageHeight = this.srcImage.getHeight();
this.newPixels = new byte[this.imageWidth + 2][this.imageHeight + 2];
this.oldPixels = new byte[this.imageWidth + 2][this.imageHeight + 2];
this.toBinaryExpanded(this.srcImage, this.imageWidth, this.imageHeight);
}
// 原画像を拡張し、細線化のための二次元二値データを生成するメソッド
private void toBinaryExpanded(BufferedImage image, int width, int height) {
int[] rgbPixcels = new int[width * height];
// 原画像imgを一次元RGBデータrgb[]にする
PixelGrabber grabber = new PixelGrabber(image, 0, 0, width, height, rgbPixcels, 0, width);
try {
grabber.grabPixels();
} catch (InterruptedException e) {
}
// 拡張画像を作り、その二次元二値データをすべて白(0)に設定する
for (int j = 0; j < height + 2; j++) {
for (int i = 0; i < width + 2; i++) {
this.newPixels[i][j] = WHITE;
}
}
// 原画像の一次元RGBデータrgbPixcels[]を、
// 拡張画像の中央部に二次元化して書き込む
for (int j = 1; j < height + 1; j++) {
for (int i = 1; i < width + 1; i++) {
Color color = new Color(rgbPixcels[(j - 1) * width + (i - 1)]);
int redValue = color.getRed();
if (redValue < THRESHOLD) {
// 黒に設定
this.newPixels[i][j] = BLACK;
}
}
}
}
@Override
protected void paintComponent(Graphics graphics) {
int width = this.imageWidth + 2;
int height = this.imageHeight + 2;
// 原画像を画面の左側に描画
graphics.drawImage(this.srcImage, 10, 10, null);
// 細線化を実施する
do {
this.hasChanged = false;
// 描画とコピー
this.drawAndCopy(graphics, width, height);
// 左上から細線化
for (int j = 1; j < height - 1; j++) {
for (int i = 1; i < width - 1; i++) {
if (oldPixels[i][j] == BLACK) {
this.thinImage(i, j, UPPER_LEFT);
}
}
}
// 描画とコピー
this.drawAndCopy(graphics, width, height);
// 右下から細線化
for (int j = height - 2; j >= 1; j--) {
for (int i = width - 2; i >= 1; i--) {
if (this.oldPixels[i][j] == BLACK) {
this.thinImage(i, j, LOWER_RIGHT);
}
}
}
// 描画とコピー
this.drawAndCopy(graphics, width, height);
// 右上から細線化
for (int j = 1; j < height - 1; j++) {
for (int i = width - 2; i >= 1; i--) {
if (this.oldPixels[i][j] == BLACK) {
this.thinImage(i, j, UPPER_RIGHT);
}
}
}
// 描画とコピー
this.drawAndCopy(graphics, width, height);
// 左下から細線化
for (int j = height - 2; j >= 1; j--) {
for (int i = 1; i < width - 1; i++) {
if (this.oldPixels[i][j] == BLACK) {
this.thinImage(i, j, LOWER_LEFT);
}
}
}
} while (this.hasChanged);
}
// 描画とコピーのメソッド
private void drawAndCopy(Graphics graphics, int width, int height) {
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
if (this.newPixels[i][j] == BLACK) {
graphics.setColor(Color.black);
} else {
graphics.setColor(Color.white);
}
// 細線化画像を画面右側に描画
graphics.drawRect(this.imageWidth + 40 + i, 10 + j, 1, 1);
// 次の細線化のためのコピー
this.oldPixels[i][j] = this.newPixels[i][j];
}
}
}
// 細線化のメソッド
public void thinImage(int i, int j, int start) {
byte[] pixcels = new byte[8];
pixcels[0] = this.oldPixels[i - 1][j - 1];
pixcels[1] = this.oldPixels[i - 1][j];
pixcels[2] = this.oldPixels[i - 1][j + 1];
pixcels[3] = this.oldPixels[i][j + 1];
pixcels[4] = this.oldPixels[i + 1][j + 1];
pixcels[5] = this.oldPixels[i + 1][j];
pixcels[6] = this.oldPixels[i + 1][j - 1];
pixcels[7] = this.oldPixels[i][j - 1];
for (int k = start; k < start + 3; k++) {
int product = pixcels[k % 8] * pixcels[(k + 1) % 8] * pixcels[(k + 2) % 8];
int sum = pixcels[(k + 4) % 8] + pixcels[(k + 5) % 8] + pixcels[(k + 6) % 8];
if (product == 1 && sum == 0) {
// 消去する
this.newPixels[i][j] = WHITE;
this.hasChanged = true;
return;
}
}
}
}