螺钉 螺母 硬币三种物体的识别

对于区域增长和链码表的一个应用。其中实现了几个部分:可以在图像中画出外接矩形(通过区域增长找最左,最右,最上,最下这四个点。这个功能注释起来了),通过区域增长可以得到单个区域的面积,实现了对于多个物体进行链码跟踪。之后进行识别就很简单了,但是有一步没有实现。当两个物体粘连时,如何解决?

#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)); //连通点存入链表

        }
    }
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值