对于区域增长和链码表的一个应用。其中实现了几个部分:可以在图像中画出外接矩形(通过区域增长找最左,最右,最上,最下这四个点。这个功能注释起来了),通过区域增长可以得到单个区域的面积,实现了对于多个物体进行链码跟踪。之后进行识别就很简单了,但是有一步没有实现。当两个物体粘连时,如何解决?
#include<iostream>
#include<gdal_priv.h>
#include<list>
#include<vector>
using namespace std;
#define Pi 3.1415926
struct CPoint
{
int x;
int y;
CPoint& operator=(CPoint& other)
{
x=other.x;
y=other.y;
return *this;
}
CPoint(int y1,int x1)
{
x=x1;
y=y1;
}
CPoint(){};
};
struct Rect
{
CPoint m;
CPoint n;
int area;
Rect& operator=(Rect& other)
{
m = other.m;
n = other.n;
area = other.area;
return *this;
}
Rect(CPoint p1,CPoint p2,int area1)
{
m = p1;
n = p2;
area = area1;
}
Rect(int top,int bottom,int left,int right,int area1)
{
m.y = top;
m.x = left;
n.y = bottom;
n.x = right;
area = area1;
}
Rect(){};
};
/********************************************************
* 根据容差判断两像素颜色是否相近
* 参数c1、c2为两种颜色信息,delta为容差
********************************************************/
bool IsSameColor(unsigned char c1, unsigned char c2, int delta)
{
return ( abs(c1 - c2) < delta);
}
/*******************************************************************************
* 寻找下一轮廓点
* 参数:imageBuf为要搜索的图形,w、h为图像尺寸,x、y为当前边界点坐标
n为上一轮廓点到当前轮廓点的链码,color为区域的颜色
*******************************************************************************/
int NextPoint(unsigned char** imageBuf, int w, int h, int y, int x, int n, unsigned char color)
{
int directData[8][2] //链码向量表
={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};
int startIndex = n + 3 + 8; //确定起始链码,对于上一个链码方向旋转120度,开始查找下一个方向,然后顺时针一次查找八个方向,直到找到符合条件的点
int i;
//对邻域进行搜索
for(i = 0;i <= 7;i++)
{
int index = (startIndex - i) % 8;
int nx = x + directData[index][0];
int ny = y + directData[index][1];
//判断边界点
if(nx >= 0 && nx < w && ny >= 0 && ny < h)
if( IsSameColor(imageBuf[ny][nx],color,1) )
{
return index;
}
}
//如果没找到新的边界点返回错误信息
return 9;
}
/****************************************************************
* 单区域的轮廓跟踪
* 参数:imageBuf为跟踪的图像,w、h为图像尺寸,x、y为搜索起始点,
* n为起始链码,color为区域颜色
* 链码方向:
3 2 1 2
4 * 0 4 * 0
5 6 7 6
****************************************************************/
int* SingleTrack(unsigned char** imageBuf, int w, int h, int y, int x, int n, unsigned char color)
{
int directData[8][2] = {{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};//链码向量表
int* track = new int[9999]; //为链码表开辟存储单元
int k = 0;
int nx = x,ny = y;
int index = n;
//保存起始点坐标
track[0] = y;
track[1] = x;
//循环搜索下一边界点 直到回到起始点
do
{
index = NextPoint(imageBuf,w,h,ny,nx,index,color);
if(index == 9) break;
track[k+3] = index;
k++;
nx += directData[index][0];
ny += directData[index][1];
}
while( nx!=x || ny!=y );
track[2] = k; //记录链码表的长度
return track;
}
//进行物体的识别
void PatternRecognition(int Top,int Bottom,int Left,int Right,int Perimeter,int Area,unsigned char** pOutImg,int ImageWidth)
{
//周长的平方比上面积,先把硬币分离出来
float PSquPDivA = 1.0 * Perimeter * Perimeter / Area;
if (PSquPDivA > Pi * 4 - 1.5 && PSquPDivA < Pi * 4 + 1.5) //真实中1.5可以设置的大一些,5以内估计都可以
{
cout<<"Point : "<<Top<<" "<<Left<<" 是一个圆"<<endl;
return;
}
//寻找外接正方形的最中心的10%,如果是全都是白色的,就为螺母,否则就是螺丝
int Width = Right - Left + 1;
int Height = Bottom - Top + 1;
int IndexI,IndexJ;
IndexI = Top + Height / 2 - Height / 10 / 2; // /与%要多注意一点
IndexJ = Left + Width / 2 - Width / 10 / 2;
for (int i = 0; i < Height / 10; i++)
{
for (int j = 0; j < Width / 10; j++)
{
if (pOutImg[0][(IndexI + i) * ImageWidth + IndexJ + j] == 0)
{
goto bolt;
}
}
}
cout<<"Point : "<<Top<<" "<<Left<<" 是一个螺母"<<endl;
return ;
bolt: //正常顺序执行也会执行这一段内容
{
cout<<"Point : "<<Top<<" "<<Left<<" 是一个螺钉"<<endl;
return ;
}
}
vector<CPoint> m_vecReoginPt;//将同一个区域的点存在这个容器中
vector<Rect> m_vecRect;
int Flag=0;
int main()
{
//函数声明
void RegionGrow(unsigned char* pInImg,unsigned char* GrowFlag, int Width,int Height,int i, int j);
void SetRed(unsigned char* pOutImg,int Width, int Height);
//注册
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
//读图像
GDALDataset* pInDataset = (GDALDataset*) GDALOpen("SingleBand.bmp",GA_ReadOnly);
int Width = pInDataset->GetRasterXSize();
int Height = pInDataset->GetRasterYSize();
int BandCount = pInDataset->GetRasterCount();
GDALRasterBand* pInRasterBand;
//输出的二值图像取反图像
GDALDriver* pReverseDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
GDALDataset* pReverseDataset = pReverseDriver->Create("Reverse.tif",Width, Height, BandCount, GDT_Byte, NULL);
GDALRasterBand* pReverseRasterBand;
//输入图像分配内存
unsigned char** data = new unsigned char* [BandCount]();
for(int i = 0; i < BandCount; ++i)
data[i] = new unsigned char [Width * Height]();
//memset( data, 0, sizeof(unsigned char) * BandCount * Width * Height);
//输出图像分配内存
unsigned char** pOutImg = new unsigned char* [BandCount]();
for(int i = 0; i < BandCount; ++i)
pOutImg[i] = new unsigned char [Width * Height]();
//设置查找标志,未遍历值为0,遍历之后值为1
unsigned char* GrowFlag = new unsigned char[Width * Height];
memset(GrowFlag, 0, sizeof(unsigned char)*Width*Height);
//单个物体的左上角和右下角的坐标
int Top,Bottom,Left,Right;
//开始逐波段的对图像进行处理
for(int i = 0; i < BandCount ; ++i)
{
//读取单波段的数据
pInRasterBand = pInDataset->GetRasterBand(i+1);
pReverseRasterBand = pReverseDataset->GetRasterBand(i+1);
CPLErr err = pInRasterBand->RasterIO( GF_Read, 0, 0, Width, Height, data[i], Width, Height, GDT_Byte, NULL, NULL);
if(err != CE_None)
cout<<"写入失败"<<endl;
//将输入图像复制到输出图像
memcpy(pOutImg[i], data[i], sizeof(unsigned char) * Width * Height);
//输入图像取反
for (int m = 0; m < Width * Height; m++)
{
pOutImg[i][m] = 255 - data[i][m];
}
//生成图像
err=pReverseRasterBand->RasterIO( GF_Write, 0, 0, Width, Height, pOutImg[i], Width, Height, GDT_Byte, NULL, NULL);
if(err != CE_None)
cout<<"图像生成失败"<<endl;
}
//输出图像
GDALDriver* pOutDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
GDALDataset* pOutDataset = pOutDriver->Create("Out.tif",Width, Height, 1, GDT_Byte, NULL);
GDALRasterBand* pOutRasterBand;
//查找独立物体的左上角坐标和右下角坐标
for(int k=0; k<Height; ++k)
for(int j=0; j<Width; ++j)
{
if(pOutImg[0][k*Width+j]==0 && GrowFlag[k*Width+j]==0)
{
RegionGrow(pOutImg[0], GrowFlag, Width, Height, k, j);
if(m_vecReoginPt.size()>1)
{
// cout<<m_vecReoginPt.size()<<endl;
// SetRed(pOutImg[i],Width,Height);
Bottom = Top = m_vecReoginPt[0].x;
Left = Right = m_vecReoginPt[0].y;
for (int m = 0; m < m_vecReoginPt.size(); m++)
{
if (Top > m_vecReoginPt[m].x)
{
Top = m_vecReoginPt[m].x;
}
if (Bottom < m_vecReoginPt[m].x)
{
Bottom = m_vecReoginPt[m].x;
}
if (Left > m_vecReoginPt[m].y)
{
Left = m_vecReoginPt[m].y;
}
if (Right < m_vecReoginPt[m].y)
{
Right = m_vecReoginPt[m].y;
}
}
cout<<".";
//画外接正方形
/* for (int m = Left; m < Right + 1; m++)
{
pOutImg[0][Top * Width + m] = 85;
pOutImg[0][Bottom * Width + m] = 85;
}
for (int m = Top; m < Bottom + 1; m++)
{
pOutImg[0][m * Width + Left] = 85 ;
pOutImg[0][m * Width + Right] = 85 ;
}
*/
m_vecRect.push_back(Rect(Top,Bottom,Left,Right,m_vecReoginPt.size()));
}
}
}
//将单波段图像转换为二维指针 注意:这里是创建了一个与原始图像一样大小的内存,使用的时候是个区域一个区域的赋值过来的
unsigned char** ImageBuffer = new unsigned char* [Height];
for (int i = 0; i < Height; i++)
{
ImageBuffer[i] = new unsigned char [Width];
memset(ImageBuffer[i],0,sizeof(unsigned char) * Width);
}
cout<<endl;
while (m_vecRect.size() > 0)
{
Top = m_vecRect.back().m.y;
Bottom = m_vecRect.back().n.y;
Left = m_vecRect.back().m.x;
Right = m_vecRect.back().n.x;
//用来标记区域中的一个边界点
int BeginI,BeginJ,BeginFlag = 0;
for (int i = 0; i < Bottom - Top + 1; i++)
{
for (int j = 0; j < Right - Left + 1; j++)
{
ImageBuffer[i][j] = pOutImg[0][(Top + i) * Width + Left + j];
if (ImageBuffer[i][j] == 0 && BeginFlag == 0)
{
BeginI = i;
BeginJ = j;
BeginFlag = 1;
}
}
}
unsigned char color = ImageBuffer[BeginI][BeginJ];
int* track = SingleTrack(ImageBuffer,Right - Left + 1,Bottom - Top + 1,BeginI,BeginJ,0,color);
cout<<track[2]<<endl;
//求取形状的周长,保存在Perimeter中
int Perimeter = 0,DiagonalNum = 0;
for (int i = 0; i < track[2]; i++) //将斜边缘乘1.414
{
if (track[i + 3] % 2 != 0)
{
DiagonalNum++;
}
Perimeter = DiagonalNum * 1.414 + track[2] - DiagonalNum;
}
cout<<Perimeter<<endl;
//进行硬币 螺钉 螺丝的识别
PatternRecognition(Top,Bottom,Left,Right,Perimeter,m_vecRect.back().area,pOutImg,Width);
cout<<endl;
m_vecRect.pop_back();
}
for (int i = 0; i < BandCount; i++)
{
delete[] data[i];
delete[] pOutImg[i];
}
for (int i = 0; i < Height; i++)
{
delete[] ImageBuffer[i];
}
delete[] data;
delete[] pOutImg;
delete[] GrowFlag;
delete[] ImageBuffer;
GDALClose(pReverseDataset);
GetGDALDriverManager()->DeregisterDriver(pReverseDriver);
GDALClose(pOutDataset);
GetGDALDriverManager()->DeregisterDriver(pOutDriver);
cout<<"处理结束"<<endl;
getchar();
return 0;
}
void RegionGrow(unsigned char* pInImg,unsigned char* GrowFlag, int Width,int Height,int k, int j)
{
int direction[8][2] = {-1, -1, 0, -1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, -1, 0};
list<CPoint>ptlist;//将符合条件的点存入链表,用于循环查找类似的点
ptlist.push_back(CPoint(j,k));
int y, x, index, y2, x2;
index = k * Width + j;
GrowFlag[index] = 1; //标记像素已遍历过
m_vecReoginPt.clear();
while(1)
{
if(ptlist.size()==0)
break;
y = ptlist.front().y;
x = ptlist.front().x;
ptlist.pop_front(); //输出第一个元素,判读其邻域
m_vecReoginPt.push_back(CPoint(y,x));
for(int i=0; i<8; i++)
{
y2 = y + direction[i][0];
x2 = x + direction[i][1];
if(y2<0 || y2>=Width || x2<0 || x2>=Height) //超出图像边界,则返回
continue;
index = x2*Width + y2;
if(pInImg[index]==255 || GrowFlag[index]==1) //像素值与连通域值不相等或已搜寻过,返回
continue;
GrowFlag[index] = 1;
ptlist.push_back(CPoint(y2, x2)); //连通点存入链表
}
}
}