Opencv4(C++)案例5:采取纯编码 不调用任何api的方式找十字中心点

需求分析

给出一幅单通道灰度图的全部灰度值,要求采用原生C++的办法对图像的像素点进行处理,找到这幅图中的十字中心点的坐标.

思路分析

实际上,这个问题是考察我们如何不用Opebcv的api来处理这个函数,考察opencv一些重要接口的底层原理或者是数字图像处理的一些基本算法。那么在这个问题中,基本思路就是区分背景色和物体色,找到一个阈值,将其二值化,将背景色设置为255,物体色设置为0,然后中心点的计算,非常简单,找到最左端灰度值为0的,依次思路找上下右,找出来mid=x:(top+down)/2 y:(left+right)/2 这就是基本思路
这道题的另外一个思路就是取前百分之x的像素点作为阈值的取值点,在C++中重载一下cmp函数到sort里面去就可以用了

编码

我的编码思路就是先对二维数组进行二值化,然后计算,设置二值化的思路是采用灰度分布直方图的方法,计算出现最高的灰度值,然后根据此为阈值二值化图像,注意,由于原图的问题,存在一些模糊和灰暗度的问题,因此阈值会出现一些问题,需要微调,直到我们调到一个合适的范围(可以借助Opencv的图像显示来调整)

AC代码

#include 
#include 
#include 
#include 
#include 

using namespace std;

const int srcRow=128;
const int srcCol=128;

void handle(int pic[][128],double &a,double &b){
    //1.十字架不一定水平与垂直
    //2.存在模糊
    //3.明暗度不同

    //阈值化分割:灰度图像阈值化分割
    //1.设置好计算的参数
    double nHistogram[256];//灰度分布直方图
    double dVariance[256];//类间方差
    int sumPic=srcRow*srcCol;
    for(int i=0;i<256;i++){
        nHistogram[i]=0.0;
        dVariance[i]=0.0;
    }

    //2.对每一个像素点进行归类
    for(int i=0;i<srcRow;i++){
        for(int j=0;j<srcCol;j++){
            nHistogram[pic[i][j]]++;//计算出频度
        }
    }

    //3.根据频度计算出概率
    double Pa=0.0;//背景出现的频率
    double Pb=0.0;//十字出现的频率
    double Wa=0.0;//背景平均灰度
    double Wb=0.0;//目标平均灰度
    double W0=0.0;//全局平均灰度
    double dData1=0.0,dData2=0.0;
    for(int i=0;i<256;i++){
        nHistogram[i] /= sumPic;//计算出频率
        W0 += i*nHistogram[i];
    }

    //4.对每个灰度值计算类间方差
    for(int i=0;i<256;i++){
        Pa += nHistogram[i];
		Pb = 1-Pa;
		dData1 += i*nHistogram[i];
		dData2 = W0-dData1;
		Wa = dData1/Pa;
		Wb = dData2/Pb;
		dVariance[i] = (Pa*Pb* pow((Wb-Wa), 2));
    }

    //5.遍历每个方差,打擂求取类间最大方差所对应的灰度值
    double temp=0;
    double nThreshold = 0;
    for(int i=0;i<256;i++){
        if(dVariance[i]>temp){
            temp=dVariance[i];
            nThreshold=i;
        }
    }
    //根据阈值二值化图像,找到十字中心
    //1.图像二值化
    for(int i=0;i<srcRow;i++){
        for(int j=0;j<srcCol;j++){
            if(pic[i][j]>= (nThreshold-19) ){
                pic[i][j]=255;
            }else{
                pic[i][j]=0;
            }
        }
    }

    //2.设置边缘点及其检测标志
    double top=srcRow-1,down=0,left=srcCol-1,right=0;//假设上下左右的那几个端点

    //3.检测最左侧的点和最右侧的点(打擂)
    for(int i=0;i<srcRow;i++){
        for(int j=0;j<srcCol;j++){//找最左侧的点
            if(pic[i][j]==0 && left>j){
                left=j;
            }
        }
        for(int j=srcCol-1;j>=0;j--){//右
            if(pic[i][j]==0 && right<j){
                right=j;
            }
        }
    }

    //4.检测最上面的点和最下面的点
    for(int i=0;i<srcCol;i++){
        for(int j=0;j<srcRow;j++){//上
            if(pic[j][i]==0 && top>j){
                top=j;
            }
        }
        for(int j=srcRow-1;j>=0;j--){//下
            if(pic[j][i]==0 && down<j){
                down=j;
            }
        }
    }

    //5.计算
    b=(top+down)/2;
    a=(right+left)/2;
    //write it
    /*
    FILE *fp;
    fp=fopen("E:/data/firsttest_output.txt","r+");
    for(int i=0;i
}

int main(){

    int input[srcRow][srcCol];
    FILE *fp;
    fp=fopen("E:/data/83 80.txt","r+");

    for(int i=0;i<srcRow;i++){
        for(int j=0;j<srcCol;j++){
            int x;
            fscanf(fp,"%d",&x);
            input[i][j]=x;
        }
    }
    double ansx,ansy;
    handle(input,ansx,ansy);
    cout<<(int)ansy<<" "<<(int)ansx;
}

总结

对于Opencv的图像处理,不仅需要灵活调用API,更需要对底层原理和数字图像处理的知识有一定认识,如此编码才更有效率。

你可能感兴趣的:(C++STL,C++入门学习,机器视觉,c++,opencv)