/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- MeasureArea
- makeWB
- init
- labeling
- checkCalcEnd
- calcStep
- getArea
- MeasureAreaThread
- getArea
- init
- labeling
- writeLabel
- run
- calc
- chase8
package jp.ac.nime.computer.grpsimulator.ImgPr;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
/** 画像計測 面積計算
* @author Kikuchi
* @version 1.0.0
*/
public class MeasureArea {
/** スレッド変数
*/
MeasureAreaThread th_;
/** 画像計測面積計算
*/
public MeasureArea() {
}
/** 白黒画像作成
* @param flag 1のとき白黒反転する
* @param imgSrc YUV形式画像
* @param imgDst YUV形式画像
*/
public void makeWB(int flag, BufferedImage imgSrc, BufferedImage imgDst) {
MeasureOp.makeWhiteBlack(flag, imgSrc, imgDst);
}
/** 計算に必要なパラメタ設定
* @param nOp 0:操作対象が黒 1:操作対象が白
* @param imgSrc YUV形式画像(二値化されていることが前提)
* @param imgDst YUV形式画像
*/
public void init(int nOp, BufferedImage imgSrc, BufferedImage imgDst) {
th_ = new MeasureAreaThread();
th_.init(nOp, imgSrc, imgDst);
}
/** 前処理 ラベリング処理
*/
public void labeling() {
th_.labeling();
th_.writeLabel();
// スレッドスタート
th_.start();
}
/** 面積計算が最後まで進んだか返す
*/
public boolean checkCalcEnd() {
return th_.isContinue();
}
/** 面積計算の探索を一回行う
*/
public void calcStep() {
if (th_.isContinue()) {
th_.waitReady(); // 子がreadyになるまでビジーウェイト
th_.next();
}
}
/** 面積データを取得
*/
public int[] getArea() {
return th_.getArea();
}
/** 面積計算スレッドクラス
*/
class MeasureAreaThread extends MeasureOp {
private int nMaxX_;
private int nMaxY_;
private int[] naLabel_; // ラベリングデータ
private int[] nPalette_;
private int nPaletteNum_;
private int nLabelMax_; // ラベル数
private int[] nArea_; // 面積配列
/** コンストラクタ
*/
public MeasureAreaThread() {
super();
nMaxX_ = 0;
nMaxY_ = 0;
}
/** 面積データを取得
*/
public int[] getArea() {
return nArea_;
}
/** 初期化 計算に必要なパラメータを全て渡す
* @param nOp 0:操作対象が黒 1:操作対象が白
* @param imgSrc YUV形式画像(二値化されていることが前提)
* @param imgDst YUV形式画像
* @param nCutoff 遮断次数
*/
public void init(int nOp, BufferedImage imgSrc, BufferedImage imgDst) {
super.init(nOp, imgSrc, imgDst);
nMaxX_ = imgSrc.getWidth();
nMaxY_ = imgSrc.getHeight();
naLabel_ = new int [nMaxX_ * nMaxY_];
// 色の設定
nPaletteNum_ = 64;
nPalette_ = new int [nPaletteNum_];
int y[] = {64 << 16, 128 << 16, 192 << 16, 255 << 16};
int u[] = { 0 << 8, 64 << 8, 192 << 8, 255 << 8};
int v[] = { 0 , 64 , 192 , 255 };
int c = 0;
for (int i = 0; i < 4; i ++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k ++) {
nPalette_[c] = y [i] | u [j] | v[k];
c ++;
}
}
}
}
/** 前処理 ラベリング処理
*/
public void labeling() {
// ラベリングデータ初期化
int nLabel = 0; // ラベル番号
Arrays.fill(naLabel_, 0);
nLabelMax_ = 0;
// 始点の検索
for (int y = 0; y < nMaxY_; y ++) {
for (int x = 0; x < nMaxX_; x ++) {
// 注目点が有効かつ未検索データだったら始点とする
if ((getPointData(x, y) == 1) && (naLabel_[x + (y * nMaxX_)] == 0)) {
// 外側境界 or 内側境界
if (getPointData(x - 1, y) == 0) {
// 外側境界探索
nLabel ++; // ラベル番号更新
chase8(x, y, 7, nLabel);
} else if (getPointData(x + 1, y) == 0) {
// 対応する外側境界のラベル番号を探す
int nSoto = 0;
for (int xx = x + 1; xx < nMaxX_; xx ++) {
nSoto = naLabel_[xx + y * nMaxX_];
if (nSoto > 0) break;
}
// 内側境界探索
chase8(x, y, 3, nSoto);
}
}
}
}
// ラベリングの個数を記憶
nLabelMax_ = nLabel;
// 面積配列
nArea_ = new int [nLabelMax_];
Arrays.fill(nArea_, 0);
}
/** ラベリングデータを出力画像に反映
*/
public void writeLabel() {
for (int y = 0; y < nMaxY_; y ++) {
for (int x = 0; x < nMaxX_; x ++) {
int nLabel = naLabel_[x + (y * nMaxX_)];
if (nLabel > 0) {
// 色で境界を塗りつぶす
int pal = (nLabel - 1) % 64;
setPointData(imgDst_, x, y, nPalette_[pal]);
}
}
}
}
/** スレッドメイン ルーチン
*/
public void run() {
int nCount = 1; // calcを呼んだ回数
while (contflag_) {
int area = calc(nCount);
nArea_[nCount - 1] = area;
//System.out.println("Label("+ (nCount - 1) + ") = " + area);
nCount ++;
if (nCount > nLabelMax_) {
contflag_ = false; // 終了
} else {
}
}
}
/** 1つの図形の面積計算
* @param nCount 対象とするラベル番号
*/
private synchronized int calc(int nLabel) {
// 境界で区切られている領域をlabelデータで塗りこむループ
for (int y = 0; y < nMaxY_; y ++) {
for (int x = 0; x < nMaxX_; x ++) {
// 注目点が有効だったら左のデータで塗りこむ
if ((getPointData(x, y) == 1) && (naLabel_[(x - 1) + (y * nMaxX_)] == nLabel)) {
naLabel_[ x + (y * nMaxX_)] = nLabel;
}
}
}
// 表示データ更新しながらWaitする
// 面積も数える
int area = 0;
boolean chk = false; // データを書き換えたか
for (int y = 0; y < nMaxY_; y ++) {
for (int x = 0; x < nMaxX_; x ++) {
int nLabel2 = naLabel_[x + (y * nMaxX_)];
if (nLabel2 == nLabel) {
// 色で境界を塗りつぶす
int pal = (nLabel2 - 1) % 64;
setPointData(imgDst_, x, y, nPalette_[pal]);
// Area
area ++;
chk = true;
}
}
// ボタン待ち
if (chk) {
waitNext();
chk = false;
}
}
return area;
}
/** 8連結探索しながらラベリング
* @param x 始点x
* @param y 始点y
* @param nChase 最初の探索方向
* @param nLabel ラベル番号
*/
private void chase8(int x, int y, int nChase, int nLabel) {
int x0 = x, y0 = y;
int x1, y1, x2, y2;
boolean chk; // 探索でみつかったか
// 初期化
x1 = x0; y1 = y0;
x2 = -1; y2 = -1;
chk = false;
// 探索ループ 初期座標に帰ってくるまでのループ
while ((x2 != x0) || y2 != y0) {
chk = false;
switch (nChase) {
case 0: // 下を探索
x2 = x1;
y2 = y1 + 1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 6;
chk = true;
} else {
// 無かった
nChase = 1;
}
break;
case 1: // 右下
x2 = x1 + 1;
y2 = y1 + 1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 7;
chk = true;
} else {
// 無かった
nChase = 2;
}
break;
case 2: // 右
x2 = x1 + 1;
y2 = y1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 0;
chk = true;
} else {
// 無かった
nChase = 3;
}
break;
case 3: // 右上
x2 = x1 + 1;
y2 = y1 - 1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 1;
chk = true;
} else {
// 無かった
nChase = 4;
}
break;
case 4: // 上
x2 = x1;
y2 = y1 - 1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 2;
chk = true;
} else {
// 無かった
nChase = 5;
}
break;
case 5: // 左上
x2 = x1 - 1;
y2 = y1 - 1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 3;
chk = true;
} else {
// 無かった
nChase = 6;
}
break;
case 6: // 左
x2 = x1 - 1;
y2 = y1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 4;
chk = true;
} else {
// 無かった
nChase = 7;
}
break;
case 7: // 左下
x2 = x1 - 1;
y2 = y1 + 1;
if (getPointData(x2, y2) == 1) {
// あった
nChase = 5;
chk = true;
} else {
// 無かった
nChase = 0;
}
break;
}
// ラベリング
if (chk) {
naLabel_[x2 + y2 * nMaxX_] = nLabel;
// 座標移動
x1 = x2;
y1 = y2;
}
}
}
}
}