細線化処理を追加
ここからtrace.cpp
/* 輪郭線追跡プログラム 8連結
* 前提:画像の最外周は画素が存在しないこと(存在すると実行時エラー)
* ドーナツ型の図形もおそらく対応
* 画像は擬似配列を用いている(1が画素とする)
*/
#include<iostream>
#include"img_process.h"
using namespace std;
int OutlineTrace();//輪郭線追跡
int TraceAround(Point searchpt,int direction);//画像の周囲をラベル付けする
int Thinning(); //細線化
int gaso[SIZE][SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,
0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
const Point offset[8] = { {1,0},{1,-1}, //8近傍の相対位置(オフセット)
{0,-1},{-1,-1},
{-1,0},{-1,1},
{0,1},{1,1}
};
int main()
{
output();
// Thinning(); //細線化するときはこっち
OutlineTrace(); //輪郭線追跡する時はこっち
output();
}
void output() //擬似画像の出力
{
for(int i = 0 ; i < SIZE ; i++){
for(int j = 0 ; j < SIZE ; j++){
if(gaso[i][j] == WHITE)
cout << "○";
else if(gaso[i][j] == BLACK)
cout << "●";
else
cout << "▲";
}
cout << endl;
}
cout << endl;
}
int OutlineTrace(){
Point searchpt;
for(searchpt.y; searchpt.y < SIZE ; searchpt.y++){
for(searchpt.x = 0 ; searchpt.x < SIZE ; searchpt.x++){
if(gaso[searchpt.y][searchpt.x] == BLACK){ //BLACKがあれば周りを走査
// cout << "画素" << searchpt.y << "," << searchpt.x << "からトレース開始" << endl;
TraceAround(searchpt,0);
// output();
}
if(gaso[searchpt.y][searchpt.x] == CHECK){ //CHECK済みであれば次のWHITEまで読み飛ばし
while(gaso[searchpt.y][searchpt.x] != WHITE){
searchpt.x++;
}
}
}
}
return 0;
}
//画像の周りをトレース
/*
Pを取り囲む要素 Pの周囲の座標
p3 p2 p1 P+offset[3] P+offset[2] P+offset[1]
p4 P p0 P+offset[4] P P+offset[0]
p5 p6 p7 P+offset[5] P+offset[6] P+offset[7]
画素を発見した位置(direction)と探索を開始する位置(sd)には
sd = (direction + 5) % 8
の関係がある
*/
int TraceAround(Point pt,int direction){
Point startpt = pt; //開始地点を保存
gaso[pt.y][pt.x] = CHECK;
do{
int sd = (direction + 5) % 8; //探索を開始する方向(SearchDirection)
for(int i = 0;i < 8; ++i){ //ここでもし周りに何も見つからなかったら無限ループになるので後で修正
Point arroundpt = pt + offset[(sd + i) % 8];
if(gaso[arroundpt.y][arroundpt.x] != WHITE){
pt = arroundpt;
gaso[pt.y][pt.x] = CHECK;
// cout << "画素" << pt.y << "," << pt.x << "をチェック" << endl;
direction = (sd + i) % 8;
break;
}
}
}while(!(startpt == pt )); //開始地点に戻ってくるまでループ
return 0;
}
ここからthinning.cpp
/* 細線化処理プログラム 8連結
* Hilditchの方法
* 前提:画像の最外周は画素が存在しないこと(存在すると実行時エラー)
* 画像は擬似配列を用いている(1が画素とする)
*/
#include<iostream>
#include"img_process.h"
#include<conio.h>
using namespace std;
bool Isgaso(const Point pt); //条件1:対象領域の画素である条件
bool Isborder(const Point pt); //条件2:境界画素である条件
bool IsNotContract(Point pt); //条件3:端点を削除しない条件(接点でなければ真)
bool IsNotIsolate(Point pt); //条件4:孤立点を削除しない条件(孤立点でなければ真) ・・・なくても動く?
bool IsPreserveContract(Point pt); //条件5:連結性を保存する条件
bool IsPreserveContract_saveDepth2(Point pt); //条件6:線幅が2の線分に対して,片側のみ削除する条件
int Nc8(Point pt); //条件5に書いてあるNc8を求める
void ClearCheck(void); //CHECKを全てWHITEに変える
const Point offset[8] = { {1,0},{1,-1}, //8近傍の相対位置(オフセット)
{0,-1},{-1,-1},
{-1,0},{-1,1},
{0,1},{1,1}
};
//細線化
int Thinning(){
bool checkflag = false; //一つでも画素がチェックされればTRUE
do{
checkflag = false;
Point searchpt;
for(searchpt.y = 0 ; searchpt.y < SIZE ; searchpt.y++){
for(searchpt.x = 0 ; searchpt.x < SIZE ; searchpt.x++){
if(Isgaso(searchpt) && Isborder(searchpt) && IsNotContract(searchpt)
&& IsNotIsolate(searchpt) && IsPreserveContract(searchpt) && IsPreserveContract_saveDepth2(searchpt)){
checkflag = true; //チェックフラグを立てる
gaso[searchpt.y][searchpt.x] = CHECK;
}
}
}
output();
ClearCheck(); //CHECKを全てWHITEに変える
getch();
}while(checkflag); //一つもCHECKが存在しなければ終了
return 0;
}
//条件1:対象領域の画素である条件
bool Isgaso(const Point pt){
return gaso[pt.y][pt.x] ? true : false ;
}
//条件2:境界画素である条件
bool Isborder(Point pt){
int cnt = 0;
for(int i=0;i < 8;i+=2){
Point arroundpt = pt + offset[i];
if(gaso[arroundpt.y][arroundpt.x] == WHITE){
cnt++;
}
}
return cnt ? true : false; //cnt > 0で真
}
//条件3:端点を削除しない条件(接点でなければ真)
bool IsNotContract(Point pt){
int cnt = 0;
for(int i=0;i < 8;++i){
Point arroundpt = pt + offset[i];
if(gaso[arroundpt.y][arroundpt.x] == BLACK || gaso[arroundpt.y][arroundpt.x] == CHECK){
cnt++;
}
}
return cnt >= 2? true : false;
}
//条件4:孤立点を削除しない条件(孤立点でなければ真)
bool IsNotIsolate(Point pt){
for(int i=0;i < 8;++i){
Point arroundpt = pt + offset[i];
if(gaso[arroundpt.y][arroundpt.x] == BLACK){
return true;
}
}
return false;
}
//条件5:連結性を保存する条件
bool IsPreserveContract(Point pt){
return Nc8(pt) == 1? true : false;
}
//条件6:線幅が2の線分に対して,片側のみ削除する条件
bool IsPreserveContract_saveDepth2(Point pt){
for(int i = 0;i < 8;++i){
Point arroundpt = pt + offset[i];
if(gaso[arroundpt.y][arroundpt.x] == CHECK){
// Point copypt = arroundpt; //一時的に保存
gaso[arroundpt.y][arroundpt.x] = WHITE; //B(Pi) = 0としてNc8(pt)を計算する
if(Nc8(pt) != 1){
gaso[arroundpt.y][arroundpt.x] = CHECK; //元に戻す
return false;
}
gaso[arroundpt.y][arroundpt.x] = CHECK; //元に戻す
}
}
return true;
}
//条件5に書いてあるNc8を求める
int Nc8(Point pt){
int cnt = 0;
for(int i = 0;i < 8;i += 2){
Point Pk[3] = { pt + offset[i],pt + offset[(i + 1) % 8],pt + offset[(i + 2) % 8]
}; //Pk,Pk+1,Pk+2を求める
int cp[3]; //C^(P) = 1 - |C(P)|
for(int j = 0;j < 3;++j){
cp[j] = 1 - abs(gaso[Pk[j].y][Pk[j].x]);
}
cnt += cp[0] - cp[0] * cp[1] * cp[2]; //C(Pk)-C(Pk)C(Pk+1)C(Pk+2)
}
return cnt;
}
//全てのCHECKをWHITEに変える
void ClearCheck(){
Point searchpt;
for(searchpt.y = 0 ; searchpt.y < SIZE ; searchpt.y++){
for(searchpt.x = 0 ; searchpt.x < SIZE ; searchpt.x++){
if(gaso[searchpt.y][searchpt.x] == CHECK){
gaso[searchpt.y][searchpt.x] = WHITE;
}
}
}
}
ここからimg_process.h
#ifndef IMG_PROCESS_H
#define IMG_PROCESS_H
#define SIZE 20
#define WHITE 0
#define BLACK 1
#define CHECK -1
//絶対値を返すマクロ
//#define abs(x) ( (x > 0) ? x : -x )
void output(void); //擬似画像表示関数
extern gaso[SIZE][SIZE]; //擬似画像
class Point{ //座標クラス
public:
int x;
int y;
Point operator+(Point pt){
Point tmp;
tmp.x = x + pt.x;
tmp.y = y + pt.y;
return tmp;
}
bool operator==(Point pt){
return x == pt.x && y == pt.y;
}
};
#endif