/*
* PROJECT: NyARToolkit
* --------------------------------------------------------------------------------
* This work is based on the original ARToolKit developed by
* Hirokazu Kato
* Mark Billinghurst
* HITLab, University of Washington, Seattle
* http://www.hitl.washington.edu/artoolkit/
*
* The NyARToolkit is Java edition ARToolKit class library.
* Copyright (C)2008-2009 Ryo Iizuka
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* For further information please contact.
* http://nyatla.jp/nyatoolkit/
* <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
*
*/
package jp.nyatla.nyartoolkit.core.squaredetect;
import jp.nyatla.nyartoolkit.NyARException;
import jp.nyatla.nyartoolkit.core.raster.*;
import jp.nyatla.nyartoolkit.core.types.*;
/**
* このクラスは、輪郭線の抽出クラスです。
* 画像中の1点を開始点として、8方位探索で輪郭線を抽出します。出力は輪郭点の配列です。
* <p>入力できる画素形式 - {@link #getContour}に入力できる画素形式に制限があります。<br/>
* {@link NyARBinRaster}
* <ul>
* <li>{@link NyARBufferType#INT1D_BIN_8}
* </ul>
* {@link NyARGrayscaleRaster}
* <ul>
* <li>{@link NyARBufferType#INT1D_GRAY_8}
* </ul>
* </p>
*/
public class NyARContourPickup
{
//巡回参照できるように、テーブルを二重化
// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6
/** 8方位探索の座標マップ*/
protected final static int[] _getContour_xdir = { 0, 1, 1, 1, 0,-1,-1,-1 , 0, 1, 1, 1, 0,-1,-1};
/** 8方位探索の座標マップ*/
protected final static int[] _getContour_ydir = {-1,-1, 0, 1, 1, 1, 0,-1 ,-1,-1, 0, 1, 1, 1, 0};
/**
* この関数は、ラスタの指定点を基点に、輪郭線を抽出します。
* 開始点は、輪郭の一部である必要があります。
* 通常は、ラべリングの結果の上辺クリップとX軸エントリポイントを開始点として入力します。
* @param i_raster
* 輪郭線を抽出するラスタを指定します。
* @param i_entry_x
* 輪郭抽出の開始点です。
* @param i_entry_y
* 輪郭抽出の開始点です。
* @param o_coord
* 輪郭点を格納するオブジェクトを指定します。
* @return
* 輪郭線がo_coordの長さを超えた場合、falseを返します。
* @throws NyARException
*/
public boolean getContour(NyARBinRaster i_raster,int i_entry_x,int i_entry_y,NyARIntCoordinates o_coord) throws NyARException
{
assert(i_raster.isEqualBufferType(NyARBufferType.INT1D_BIN_8));
NyARIntSize s=i_raster.getSize();
return impl_getContour(i_raster,0,0,s.w-1,s.h-1,0,i_entry_x,i_entry_y,o_coord);
}
/**
* この関数は、ラスタの指定点を基点に、画像の特定の範囲内から輪郭線を抽出します。
* 開始点は、輪郭の一部である必要があります。
* 通常は、ラべリングの結果の上辺クリップとX軸エントリポイントを開始点として入力します。
* @param i_raster
* 輪郭線を抽出するラスタを指定します。
* @param i_area
* 輪郭線の抽出範囲を指定する矩形。i_rasterのサイズ内である必要があります。
* @param i_entry_x
* 輪郭抽出の開始点です。
* @param i_entry_y
* 輪郭抽出の開始点です。
* @param o_coord
* 輪郭点を格納するオブジェクトを指定します。
* @return
* 輪郭線がo_coordの長さを超えた場合、falseを返します。
* @throws NyARException
*/
public boolean getContour(NyARBinRaster i_raster,NyARIntRect i_area,int i_entry_x,int i_entry_y,NyARIntCoordinates o_coord) throws NyARException
{
assert(i_raster.isEqualBufferType(NyARBufferType.INT1D_BIN_8));
return impl_getContour(i_raster,i_area.x,i_area.y,i_area.x+i_area.w-1,i_area.h+i_area.y-1,0,i_entry_x,i_entry_y,o_coord);
}
/**
* この関数は、ラスタの指定点を基点に、輪郭線を抽出します。
* 開始点は、輪郭の一部である必要があります。
* 通常は、ラべリングの結果の上辺クリップとX軸エントリポイントを開始点として入力します。
* @param i_raster
* 輪郭線を抽出するラスタを指定します。
* @param i_th
* 輪郭とみなす暗点の敷居値を指定します。
* @param i_entry_x
* 輪郭抽出の開始点です。
* @param i_entry_y
* 輪郭抽出の開始点です。
* @param o_coord
* 輪郭点を格納する配列を指定します。i_array_sizeよりも大きなサイズの配列が必要です。
* @return
* 輪郭の抽出に成功するとtrueを返します。輪郭抽出に十分なバッファが無いと、falseになります。
* @throws NyARException
*/
public boolean getContour(NyARGrayscaleRaster i_raster,int i_th,int i_entry_x,int i_entry_y,NyARIntCoordinates o_coord) throws NyARException
{
assert(i_raster.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
NyARIntSize s=i_raster.getSize();
return impl_getContour(i_raster,0,0,s.w-1,s.h-1,i_th,i_entry_x,i_entry_y,o_coord);
}
/**
* この関数は、ラスタの指定点を基点に、画像の特定の範囲内から輪郭線を抽出します。
* 開始点は、輪郭の一部である必要があります。
* 通常は、ラべリングの結果の上辺クリップとX軸エントリポイントを開始点として入力します。
* @param i_raster
* 輪郭線を抽出するラスタを指定します。
* @param i_area
* 輪郭線の抽出範囲を指定する矩形。i_rasterのサイズ内である必要があります。
* @param i_th
* 輪郭とみなす暗点の敷居値を指定します。
* @param i_entry_x
* 輪郭抽出の開始点です。
* @param i_entry_y
* 輪郭抽出の開始点です。
* @param o_coord
* 輪郭点を格納するオブジェクトを指定します。
* @return
* 輪郭線がo_coordの長さを超えた場合、falseを返します。
* @throws NyARException
*/
public boolean getContour(NyARGrayscaleRaster i_raster,NyARIntRect i_area,int i_th,int i_entry_x,int i_entry_y,NyARIntCoordinates o_coord) throws NyARException
{
assert(i_raster.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
return impl_getContour(i_raster,i_area.x,i_area.y,i_area.x+i_area.w-1,i_area.h+i_area.y-1,i_th,i_entry_x,i_entry_y,o_coord);
}
/**
* 輪郭線抽出関数の実体です。
* @param i_raster
* @param i_l
* @param i_t
* @param i_r
* @param i_b
* @param i_th
* @param i_entry_x
* @param i_entry_y
* @param o_coord
* @return
* @throws NyARException
*/
private boolean impl_getContour(INyARRaster i_raster,int i_l,int i_t,int i_r,int i_b,int i_th,int i_entry_x,int i_entry_y,NyARIntCoordinates o_coord) throws NyARException
{
assert(i_t<=i_entry_x);
NyARIntPoint2d[] coord=o_coord.items;
final int[] xdir = _getContour_xdir;// static int xdir[8] = { 0, 1, 1, 1, 0,-1,-1,-1};
final int[] ydir = _getContour_ydir;// static int ydir[8] = {-1,-1, 0, 1, 1, 1, 0,-1};
final int[] buf=(int[])i_raster.getBuffer();
final int width=i_raster.getWidth();
//クリップ領域の上端に接しているポイントを得る。
int max_coord=o_coord.items.length;
int coord_num = 1;
coord[0].x = i_entry_x;
coord[0].y = i_entry_y;
int dir = 5;
int c = i_entry_x;
int r = i_entry_y;
for (;;) {
dir = (dir + 5) % 8;//dirの正規化
//ここは頑張ればもっと最適化できると思うよ。
//4隅以外の境界接地の場合に、境界チェックを省略するとかね。
if(c>i_l && c<i_r && r>i_t && r<i_b){
for(;;){//gotoのエミュレート用のfor文
//境界に接していないとき(暗点判定)
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
dir++;
if (buf[(r + ydir[dir])*width+(c + xdir[dir])] <= i_th) {
break;
}
//8方向全て調べたけどラベルが無いよ?
throw new NyARException();
}
}else{
//境界に接しているとき
int i;
for (i = 0; i < 8; i++){
final int x=c + xdir[dir];
final int y=r + ydir[dir];
//境界チェック
if(x>=i_l && x<=i_r && y>=i_t && y<=i_b){
if (buf[(y)*width+(x)] <= i_th) {
break;
}
}
dir++;//倍長テーブルを参照するので問題なし
}
if (i == 8) {
//8方向全て調べたけどラベルが無いよ?
throw new NyARException();// return(-1);
}
}
// xcoordとycoordをc,rにも保存
c = c + xdir[dir];
r = r + ydir[dir];
coord[coord_num].x = c;
coord[coord_num].y = r;
//終了条件判定
if (c == i_entry_x && r == i_entry_y){
//開始点と同じピクセルに到達したら、終点の可能性がある。
coord_num++;
//末端のチェック
if (coord_num == max_coord) {
//輪郭bufが末端に達した
return false;
}
//末端候補の次のピクセルを調べる
dir = (dir + 5) % 8;//dirの正規化
int i;
for (i = 0; i < 8; i++){
final int x=c + xdir[dir];
final int y=r + ydir[dir];
//境界チェック
if(x>=i_l && x<=i_r && y>=i_t && y<=i_b){
if (buf[(y)*width+(x)] <= i_th) {
break;
}
}
dir++;//倍長テーブルを参照するので問題なし
}
if (i == 8) {
//8方向全て調べたけどラベルが無いよ?
throw new NyARException();
}
//得たピクセルが、[1]と同じならば、末端である。
c = c + xdir[dir];
r = r + ydir[dir];
if(coord[1].x ==c && coord[1].y ==r){
//終点に達している。
o_coord.length=coord_num;
break;
}else{
//終点ではない。
coord[coord_num].x = c;
coord[coord_num].y = r;
}
}
coord_num++;
//末端のチェック
if (coord_num == max_coord) {
//輪郭が末端に達した
return false;
}
}
return true;
}
}