It's OpenCV

OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。

OpenCV可用于开发实时的图像处理、计算机视觉以及模式识别程序,且授权可以在商业和研究领域中免费使用。

OpenCV用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口。该库也有大量的Python, Java andMATLAB/OCTAVE的接口。

优点显而易见,而OpenCV目前最大的缺点就是,不能处理透明通道类似Png。

 

OpenCV第一步,显示一张图片

opencv中常见的与图像操作有关的数据容器有Mat,cvMat和IplImage,这三种类型都可以代表和显示图像,

Mat类型侧重于计算,数学性较高,openCV对Mat类型的计算也进行了优化

而CvMat和IplImage类型更侧重于“图像”,opencv对其中的图像操作(缩放、单通道提取、图像阈值操作等)进行了优化。

下面先采用Mat来记录图片。

算术运算:cv::add(), cv::addWeighted(), cv::scaleAdd(); cv::subtract, cv::absdiff; cv::multiply; cv::divide, 还可以通过mask参数来掩模不需要处理的位。

位运算:cv::bitwise_and, cv::bitwise_or, cv::bitwise_xor, cv::bitwise_not

其他运算:cv::sqrt, cv::pow, cv::abs, cv::cuberoot, cv::exp, cv::log

inv()求逆、t()求转置、determinant()求行列式、norm()求矢量长度、cross()求两个向量的叉乘、dot()求两个向量的点乘。

#include "highgui.h"
using namespace cv;
int main() { 
    char* imagename = "C:\\Users\\PCforSONG\\Desktop\\test.jpg";
    //Mat img =  imread(imagename);
    Mat img(240,320,CV_8U,Scalar(100)); //大小是320*240,图像上每个点的数据类型是无符号8位的,并用100填充所有像素点
    if(img.empty())
        return -1; //是否加载成功
    if(!img.data)return -1;
    namedWindow("图片", CV_WINDOW_AUTOSIZE);
    imshow("图片", img);
    Mat new_img=img.clone(); //创建一个副本
    
    Mat newImage;
    newImage.create(new_img.size(),new_img.type()); //创建一个跟原图像大小相同的图像
    imwrite(imagename,newImage);
    waitKey(5000);
    return 0;
}

 

newImage.create(new_img.size(),new_img.type()) || result.create(image.rows,image.cols,image.type())

new_img.type():

• CV_8U - 8-bit unsigned integers ( 0..255 )

• CV_8S - 8-bit signed integers ( -128..127 )

• CV_16U - 16-bit unsigned integers ( 0..65535 )

• CV_16S - 16-bit signed integers ( -32768..32767 )

• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )

• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )

• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

 

 

使用另一种图片处理类型 IplImage 

 cvResize就可以将源图片转成新图片的大小

#include <cv.h>
#include <highgui.h>
#include <cmath>

int main() {
    float scale = 0.3;  //缩放倍数为0.618倍

    CvSize dst_cvsize;   //目标图像尺寸

    IplImage *src = cvLoadImage("C:\\Users\\PCforSONG\\Desktop\\Test2.jpg");
    dst_cvsize.width = src->width * scale;  //目标图像的宽为源图象宽的scale倍

    dst_cvsize.height = src->height * scale; //目标图像的高为源图象高的scale倍

    IplImage *dst = cvCreateImage( dst_cvsize, src->depth, src->nChannels); //构造目标图象

    cvResize(src, dst, CV_INTER_LINEAR); //缩放源图像到目标图像
    cvSaveImage("C:\\Users\\PCforSONG\\Desktop\\Test3.jpg", dst);
    cvNamedWindow( "src",   CV_WINDOW_AUTOSIZE );
    cvNamedWindow( "dst",   CV_WINDOW_AUTOSIZE );
    cvShowImage( "src", src );  //显示源图像
    cvShowImage( "dst", dst );  //显示目标图像
    cvWaitKey(-1);

    return 0;
}

 

 

第二步,图像降色

图像遍历方法

image.at<uchar>(i,j):取出灰度图像中i行j列的点。

image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点

image.ptr<uchar>(image.rows): 每行数据的指针,每行有个地址 image.cols*image.channels()

MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>(), itend_out=outImage.end<Vec3b>(): 迭代器访问每个点

 

#include "highgui.h"
using namespace cv;

void colorReduce(Mat& image,int div) { //将每个通道的256种颜色用div种代替,每个颜色段取中间的颜色值作为代表色
    for(int i=0;i<image.rows;i++) {
        for(int j=0;j<image.cols;j++) {
            //image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点
            image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;
            image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;
            image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;
        }
    }
}
int main() { 
    char* imagename = "C:\\Users\\PCforSONG\\Desktop\\test.jpg";
    Mat img =  imread(imagename);
    if(img.empty())
        return -1; //是否加载成功
    if(!img.data)return -1;
    namedWindow("图片", CV_WINDOW_AUTOSIZE);
    //imshow("图片", img);
    colorReduce(img, 64);
    imshow("图片", img);
    waitKey(5000);
    return 0;
}

 

OpenCV中默认的像素点Vec3b由BRG uchar数组[3]表示

降色至64色结果:

 

使用另一种图片类型IplImage 

cvResize就可以实现对图片的压缩

#include <cv.h>
#include <highgui.h>
#include <cmath>

int main() {
    float scale = 0.3;  //缩放倍数为0.618倍

    CvSize dst_cvsize;   //目标图像尺寸

    IplImage *src = cvLoadImage("C:\\Users\\PCforSONG\\Desktop\\Test2.jpg");    //载入工作目录下文件名为“tiger.jpg”的图片。
    dst_cvsize.width = src->width * scale;  //目标图像的宽为源图象宽的scale倍

    dst_cvsize.height = src->height * scale; //目标图像的高为源图象高的scale倍

    IplImage *dst = cvCreateImage( dst_cvsize, src->depth, src->nChannels); //构造目标图象

    cvResize(src, dst, CV_INTER_LINEAR); //缩放源图像到目标图像
    cvSaveImage("C:\\Users\\PCforSONG\\Desktop\\Test3.jpg", dst);
    cvNamedWindow( "src",   CV_WINDOW_AUTOSIZE );
    cvNamedWindow( "dst",   CV_WINDOW_AUTOSIZE );
    cvShowImage( "src", src );  //显示源图像
    cvShowImage( "dst", dst );  //显示目标图像
    cvWaitKey(-1);  //等待用户响应

    return 0;
}

 

 

OpenCV第三步,临域操作图像锐化

#include "highgui.h"
using namespace cv;

void ImgFilter2d(const Mat &image,Mat& result) {
    result.create(image.size(),image.type());
    int nr=image.rows;
    int nc=image.cols*image.channels();
    //滤波算子为[0 –1 0;-1 5 –1;0 –1 0]
    //核心公式即:sharp(i.j)=5*image(i,j)-image(i-1,j)-image(i+1,j)
    for(int i=1;i<nr-1;i++) {
        const uchar* up_line=image.ptr<uchar>(i-1);//指向上一行
        const uchar* mid_line=image.ptr<uchar>(i);//当前行
        const uchar* down_line=image.ptr<uchar>(i+1);//下一行
        uchar* cur_line=result.ptr<uchar>(i);
        for(int j=1;j<nc-1;j++) {
            cur_line[j]=saturate_cast<uchar>(5*mid_line[j]-mid_line[j-1]-mid_line[j+1]-up_line[j]-down_line[j]);
        }
    }
    // 把图像边缘像素设置为0
    result.row(0).setTo(Scalar(0));
    result.row(result.rows-1).setTo(Scalar(0));
    result.col(0).setTo(Scalar(0));
    result.col(result.cols-1).setTo(Scalar(0));
}
int main() { 
    char* imagename = "C:\\Users\\PCforSONG\\Desktop\\test.jpg";
    Mat img =  imread(imagename);
    if(img.empty())
        return -1; //是否加载成功
    if(!img.data)return -1;
    Mat ans;
    ImgFilter2d(img, ans);
    imshow("图片", ans);
    waitKey(5000);
    return 0;
}

 

 

OpenCV第四步,PS的魔棒功能

cvtColor(A,B,CV_BGR2Lab); 将A颜色模式转为CV_BGR2Lab存入B

cvtColor(Image,Image,CV_BGR2GRAY); 转为灰度图

#include "highgui.h"
#include "cv.h"
using namespace cv;

class colorDetect {
private:
    int minDist; //阀值
    Vec3b target; //目标颜色    
public:
    Mat result;
    colorDetect() {
    };
    void SetMinDistance(int dist) {
        minDist = dist;
    };
    void SetTargetColor(uchar red,uchar green,uchar blue) {
        target[0] = blue, target[1] = red, target[2] = green;
    };
    void SetTargetColor(Vec3b color);
    Mat process(const Mat& image);
};
Mat colorDetect::process(const Mat& image) {
    Mat ImageLab=image.clone();
    result.create(image.rows,image.cols,CV_8U);
    cvtColor(image,ImageLab,CV_BGR2Lab); //将image转换为Lab格式存储在ImageLab中
    Mat temp(1,1,CV_8UC3); //创建一张1*1的临时图像
    temp.at<Vec3b>(0,0)=target; //目标颜色填充
    cvtColor(temp,temp,CV_BGR2Lab); //将由默认的BGR转换为Lab
    target=temp.at<Vec3b>(0,0); //再从临时图像的Lab格式中取出Lab格式的目标颜色

    // 创建处理用的迭代器
    Mat_<Vec3b>::iterator it=ImageLab.begin<Vec3b>();
    Mat_<uchar>::iterator itout=result.begin<uchar>();
    while(it!=ImageLab.end<Vec3b>()) {
        //两个颜色值之间距离的计算
        int dist=static_cast<int>(norm<int,3>(Vec3i((*it)[0]-target[0],
            (*it)[1]-target[1],(*it)[2]-target[2])));
        if(dist<minDist)
            (*itout)=255;
        else
            (*itout)=0;
        it++;
        itout++;
    }
    return result;
}
int main() { 
    char* imagename = "C:\\Users\\PCforSONG\\Desktop\\test.jpg";
    Mat img =  imread(imagename);
    if(img.empty())
        return -1; //是否加载成功
    if(!img.data)return -1;
    colorDetect OP;
    OP.SetMinDistance(80);
    OP.SetTargetColor(0, 255, 0);
    OP.process(img);
    imshow("图片", OP.result);
    waitKey(50000);
    return 0;
}

 

1,在将图像转换为Lab空间,cvtColor(Old,New,type),将Old转换为type格式(见第一步type())存储在New中。

2,判断两个颜色之间的距离运算了norm函数求向量长度,它的运算是norm<typename,dim>(v)。其中v是一个dim维的向量。

下面为对实例图片提取红色的结果:

 

OpenCV第五步,直方图和直方图变换

图像直方图可以反映出图像对比度,明暗程度等特征,所以我们可以利用直方图的变换进行图像画面的调节。

OpenCV中计算图像直方图像函数是calcHist,它的参数比较多,下面分析一下它的接口和用法。

void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )

const Mat* images:为输入图像的指针。

int nimages:要计算直方图的图像的个数。此函数可以为多图像求直方图,我们通常情况下都只作用于单一图像,所以通常nimages=1const int* channels:图像的通道,它是一个数组,如果是灰度图像则channels[1]={0};如果是彩色图像则channels[3]={0,1,2};如果是只是求彩色图像第2个通道的直方图,则channels[1]={1};

IuputArray mask:是一个遮罩图像用于确定哪些点参与计算,实际应用中是个很好的参数,默认情况我们都设置为一个空图像,即:Mat()。

OutArray hist:计算得到的直方图

int dims:得到的直方图的维数,灰度图像为1维,彩色图像为3维。

const int* histSize:直方图横坐标的区间数。如果是10,则它会横坐标分为10份,然后统计每个区间的像素点总和。

const float** ranges:这是一个二维数组,用来指出每个区间的范围。

 

实例代码

#include "cv.h"
#include "highgui.h"
#include <cstdio>
using namespace cv;

Mat getHistImg(const MatND& hist) {
    double maxVal=0;
    double minVal=0;

    //找到直方图中的最大值和最小值
    minMaxLoc(hist,&minVal,&maxVal,0,0);
    int histSize=hist.rows;
    Mat histImg(histSize,histSize,CV_8U,Scalar(255));
    // 设置最大峰值为图像高度的90%
    int hpt=static_cast<int>(0.9*histSize);

    for(int h=0;h<histSize;h++) {
        float binVal=hist.at<float>(h);
        int intensity=static_cast<int>(binVal*hpt/maxVal);
        line(histImg,Point(h,histSize),Point(h,histSize-intensity),Scalar::all(0));
    }
    imshow("直方图", histImg);
    //找到直方图最大最小值对应的灰度值
    int imax,imin;
    for(imin=0;imin<256;imin++)
        if(hist.at<float>(imin)==minVal)
            break;
    for(imax=255;imax>=0;imax--)
        if(hist.at<float>(imax)==maxVal)
            break;
    //创建灰度变换表
    Mat lut(1,256,CV_8U);
    for(int i=0;i<256;i++) {
        if(lut.at<uchar>(i)<imin)
            lut.at<uchar>(i)=imin;
        else if(lut.at<uchar>(i)>imax)
            lut.at<uchar>(i)=imax;
        else
            lut.at<uchar>(i)=static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5);
    }
    return lut;
}

int main() {
    Mat Image=imread("C:\\Users\\PCforSONG\\Desktop\\test.jpg");
    cvtColor(Image,Image,CV_BGR2GRAY); //转为灰度图
    //imshow("图片", Image);

    const int channels[1]={0};
    //如果是灰度图像则channels[1]={0};如果是彩色图像则channels[3]={0,1,2};如果是只是求彩色图像第2个通道的直方图,则channels[1]={1}
    const int histSize[1]={256};
    float hranges[2]={0,255};
    const float* ranges[1]={hranges};
    MatND hist;
    calcHist(&Image,1,channels,Mat(),hist,1,histSize,ranges);
    //calcHist(输入图像指针, 几图像求直方图, 通道, 遮罩, 得到的直方图,直方图的维数灰1彩3, 横坐标的区间数, 区间的范围)

    Mat result;
    LUT(Image,getHistImg(hist),result);
    //通过得到的lut灰度替换表,让灰度平均分布,对比度增强
    imshow("直方图变换", result);
    waitKey(5000);
    return 0;
}

 

 

OpenCV第六步,鼠标绘图函数

这一次使用IplImage结构来存储图片。

cvSetMouseCallback("鼠标绘图", func, (void*)img); 返回某窗口的鼠标事件传参给func函数,最后一个参数为用户自己定义,本例需传递图片指针

void func(int event, int x, int y, int extra_event, void* img) 鼠标消息传入函数参数,第一个参数为鼠标事件(按下,移动,松开),第二三个为鼠标坐标,第四个为附加事件。

#include "cv.h"
#include "highgui.h"
using namespace std;

void func(int event, int x, int y, int extra_event, void* img) { //鼠标消息传入函数参数
    static bool MouseLButtonDown = 0;
    static CvPoint from = cvPoint(0, 0);

    switch (event) {
    case CV_EVENT_LBUTTONDOWN:
        MouseLButtonDown = 1;
        from = cvPoint(x, y);
        break;

    case  CV_EVENT_LBUTTONUP:
        MouseLButtonDown = 0;
        break;

    case CV_EVENT_MOUSEMOVE:
        if (MouseLButtonDown) {
            CvPoint to = cvPoint(x, y);
            cvLine((IplImage*)img, from, to, CV_RGB(0, 0, 0), 2); //在img上两点绘制粗为2的直线
            from = to;
            cvShowImage("鼠标绘图", (IplImage*)img);
        }
        break;
    }
}
int main() {
    IplImage *img =  cvCreateImage(cvSize(1080, 768), IPL_DEPTH_8U, 3);
    cvSet(img, CV_RGB(255, 255, 255)); //用cvSet()将图像填充成白色
    cvNamedWindow("鼠标绘图", CV_WINDOW_AUTOSIZE); //创建名字为的窗口
    cvShowImage("鼠标绘图", img); //在指定名字的窗口显示图片

    cvSetMouseCallback("鼠标绘图", func, (void*)img);

    do {
        int key = cvWaitKey(0);
        switch (key) {
        case 'r':
            cvSet(img, CV_RGB(255, 255, 255));
            cvShowImage("鼠标绘图", img);
            break;

        case 's':
            cvSaveImage("已绘图片.jpg", img);
            break;
        }
    }while (1);

    cvDestroyWindow("鼠标绘图");
    cvReleaseImage(&img);
    return 0;
} 

 

为大家鼠标绘制了一张图片,12月 25日圣诞快乐!

 

 

OpenCV第七步,Sobel算子的边缘检测

canny边缘检测函数

cvCanny(SrcImage, CannyImg, threshold, threshold * 3, 3)

输入图像(单通道灰度图)+输出边缘图像(单通道黑白图)+[像素的梯度大与上限值则是边缘像素 || 小于下限值则被抛弃 || 在两者之间则当这个点与高于上限值的像素点连接时保留,否则删除]+Sobel 算子阶数

threshold(result,result,40,255,THRESH_BINARY); 用来对灰度图进行二值化,THRESH_BINARY时像素值大于40的都设为255.

#include "cv.h"
#include "highgui.h"
using namespace cv;

IplImage *SrcImage, *CannyImg;

void func(int threshold) { //cvCreateTrackbar的回调函数
    cvCanny(SrcImage, CannyImg, threshold, threshold * 3, 3); //canny边缘检测
    //输入图像(单通道灰度图)+输出边缘图像(单通道黑白图)+像素的梯度大与上限值则是边缘像素/小于下限值则被抛弃
    //        在两者之间则当这个点与高于上限值的像素点连接时保留,否则删除+Sobel 算子阶数
    cvShowImage("边缘检测图", CannyImg);
}
int main() {
    const char *pstrImageName = "C:\\Users\\PCforSONG\\Desktop\\test.jpg";

    //从文件中载入图像的灰度图CV_LOAD_IMAGE_GRAYSCALE
    SrcImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_GRAYSCALE);
    CannyImg = cvCreateImage(cvGetSize(SrcImage), IPL_DEPTH_8U, 1);

    cvNamedWindow("边缘检测图", CV_WINDOW_AUTOSIZE);
    func(40);
    //创建trackbar并添加到指定窗口,名字+窗口名字+滑块位置+滑块最大值+回调函数,默认显示在窗口顶
    int nThresholdEdge = 40;
    cvCreateTrackbar("Threshold", "边缘检测图", &nThresholdEdge, 100, func);

    cvWaitKey(0);

    cvDestroyWindow("边缘检测图");
    cvReleaseImage(&SrcImage);
    cvReleaseImage(&CannyImg);
    return 0;
}

 

检测完边缘,当然还可以继续对边缘进行检测,如检测直线,HoughLinesP()

它的输入是一个二值的轮廓图像,即边缘检测得到的图像;它的输出是一个Vector of Vec4i,数组中的每个元素是一个4元浮点极坐标<rou,theta>,rou代表直线离坐标原点的距离,theta代表角度,前两个一组,后两个一组,表示一条直线的起点和终点的极坐标。第3和第4个参数代表步长,因为Hough变换是穷举的算法,rho表示距离的步长,theta代表角度的步长。第5个参数是一个阈值设置直接的最低投票个数。

vector<Vec4i> lines;
// 检测直线,最小投票为90,线条不短于50,间隙不小于10
HoughLinesP(contours,lines,1,CV_PI/180,80,50,10);
vector<Vec4i>::const_iterator it=lines.begin();
while(it!=lines.end()) {
    Point pt1((*it)[0],(*it)[1]);
    Point pt2((*it)[2],(*it)[3]);
    line(image,pt1,pt2,color,2);
    it++;
}

 

 

OpenCV第八步,利用直方图进行图像相似度检测

图像相似性比较是比上面直方图映射更加实用且普通的例子,前段时间淘宝或百度推出类似搜图的功能都离不开图像相似性判断这个话题,当然本文这里面不可能去深入探讨些解决方案的实现,只是利用OpenCV中的例程来简单的实现图片的匹配。 

图片需要先降维,再求出图片的直方图。

直方图的比较函数为compareHist(AImg,BImg,CV_COMP_BHATTACHARYYA),最后一个参数是两个直方图间距离计算的方法。

#include "cv.h"
#include "highgui.h"
#include <iostream>
using namespace cv;

void colorReduce(Mat& image,int div) { //将每个通道的256种颜色用div种代替,每个颜色段取中间的颜色值作为代表色
    for(int i=0;i<image.rows;i++) {
        for(int j=0;j<image.cols;j++) {
            //image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点
            image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;
            image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;
            image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;
        }
    }
}

Mat getHistImg(const Mat& hist) {
    const int channels[1]={0};
    const int histSize[1]={256};
    float hranges[2]={0,255};
    const float* ranges[1]={hranges};
    MatND histImg;
    calcHist(&hist,1,channels,Mat(),histImg,1,histSize,ranges);
    return histImg;
}

int main() {
    Mat refImg=imread("ref.png");
    imshow("zz", refImg);
    Mat image1=imread("image1.png");
    Mat image2=imread("image2.png");

    //图像颜色降至64
    colorReduce(refImg,64);
    colorReduce(image1,64);
    colorReduce(image2,64);

    MatND refH=getHistImg(refImg);
    MatND hist1=getHistImg(image1);
    MatND hist2=getHistImg(image2);

    double dist1,dist2;
    dist1=compareHist(refH,hist1,CV_COMP_BHATTACHARYYA);
    dist2=compareHist(refH,hist2,CV_COMP_BHATTACHARYYA);

    printf("%.4lf %.4lf\n", dist1, dist2);
    return 0;
}

 

OpenCV第九步,形态学操作函数

形态学实际上可以理解为一种滤波行为,所以很多地方称它为形态学滤波。我们滤波中用的滤波器(kernel)在这里被称为结构元素,结构元素往往是由一个特殊的形状构成,如:线条、矩形、圆、菱形等。我们把结构元素的中心(Anchor Point)与图像上像素点对齐,然后结构元素覆盖的领域像素就是我们要分析的像素,我们定义一种操作就形成了一种形态学运算。

#include "cv.h"
#include "highgui.h"
#include <iostream>
using namespace cv;

int main() {
    Mat image=imread("C:\\Users\\PCforSONG\\Desktop\\test2.jpg");
    cvtColor(image,image,CV_BGR2GRAY); //彩色转灰度
    Mat Img;
    morphologyEx(image,Img,MORPH_GRADIENT,Mat());
    //原理,对图像先做了一个腐蚀,再做了一次膨胀,然后将两次的结果相减
    threshold(Img,Img,40,255,THRESH_BINARY); //二值化
    namedWindow("Img");
    imshow("Img",Img);

    waitKey();
    return 0;
}

 

下面实现用形态学操作来检测角点

#include "cv.h"
#include "highgui.h"
#include <iostream>
using namespace cv;

int main() {
    Mat image=imread("C:\\Users\\PCforSONG\\Desktop\\test2.jpg");
    cvtColor(image,image,CV_BGR2GRAY); //转灰度图
    //定义结构元素,十字/菱形/正方形/X形
    Mat cross(5,5,CV_8U,Scalar(0));
    Mat diamond(5,5,CV_8U,Scalar(1));
    Mat square(5,5,CV_8U,Scalar(1));
    Mat x(5,5,CV_8U,Scalar(0));

    for(int i=0;i<5;i++) {
        cross.at<uchar>(3,i)=1;
        cross.at<uchar>(i,3)=1;
    }
    diamond.at<uchar>(0,0)=0;
    diamond.at<uchar>(0,1)=0;
    diamond.at<uchar>(1,0)=0;
    diamond.at<uchar>(4,4)=0;
    diamond.at<uchar>(3,4)=0;
    diamond.at<uchar>(4,3)=0;
    diamond.at<uchar>(4,0)=0;
    diamond.at<uchar>(4,1)=0;
    diamond.at<uchar>(3,0)=0;
    diamond.at<uchar>(0,4)=0;
    diamond.at<uchar>(0,3)=0;
    diamond.at<uchar>(1,4)=0;

    for(int i=0;i<5;i++){
        x.at<uchar>(i,i)=1;
        x.at<uchar>(4-i,i)=1;
    }
    Mat result;
    dilate(image,result,cross);
    erode(result,result,diamond);

    Mat result2;
    dilate(image,result2,x);
    erode(result2,result2,square);
    absdiff(result2,result,result);
    //二值化
    threshold(result,result,40,255,THRESH_BINARY);

    for(int i=0;i<result.rows;i++) {
        const uchar* data=result.ptr<uchar>(i);
        for(int j=0;j<result.cols;j++) {
            if(data[j] > 0) //角点图像上的白点
                circle(image,Point(j,i),8,Scalar(255,255,255));// 画圈
        }
    }
    imshow("Img", image);
    waitKey();
    return 0;
}

 

OpenCV第十步,滤波器

滤波实际上是信号处理里的一个概念,其中像素点灰度值的高低代表信号的强弱。

高频:图像中灰度变化剧烈的点。

低频:图像中平坦的,灰度变化不大的点。

根据图像的高频与低频的特征,我们可以设计相应的高通与低通滤波器,

高通滤波可以检测图像中尖锐、变化明显的地方;低通滤波可以让图像变得光滑,滤除图像中的噪声。

一、线性滤波器:邻域内的像素按照一个权重相加最后设置为当前点的灰度值(卷积)

1,blur函数:用一个点邻域内像素的平均灰度值来代替该点的灰度

  blur(image,result,cv::Size(5,5));

2,高斯模糊

平均值来代替当前的灰度值,且越靠近该像素的点提供越高的权重,这样就产生了高斯模糊滤波。它的滤波器或者叫遮罩是一个高斯分布的二维矩阵。 

  GaussianBlur(image,result,cv::Size(5,5),1.5);

参数image为输入图像,result为输出图像,Size(5,5)定义了核的大小,最后一个参数说明了高斯核的方差。

 

二、非线性滤波器:

3,中值滤波:它是取邻域内所有像素的中位数作为当前点的灰度值。

medianBlur(image,result,5);

其中最后一个参数指定了邻域的大小为5*5。中值滤波也是在实际中应用最多的平滑滤波。

 

 

转载于:https://www.cnblogs.com/updateofsimon/p/3482732.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值