核心模块:
if(src.isempty())
{
return -1;
}
namedWindow("ssss",WINDOW_AUTOSIZE);//WINDOW_AUTOSIZE自动调整大小,WINDOW_NORMAL允许修改串口大小
inshow("ssss",src);
WaitKey(0);
cv::imread();//加载图像:文件名;加载图像类型(IMREAD_UNCHANGED<0、IMREAD_GRAYSCALE=0、IMREAD_COLOR>0)
cv::cvtColor();//修改图像:原图;转换后色彩空间的图;转换到方向
cv::imwrite();//保存图像:路径+文件名,按照后缀去保存类型;带保存的图像。
if(!src.data)//判断Mat里面有没有数据
{
}
矩阵的掩膜(mask也成为Kernel),也就是卷积,对应位置权重乘之后相加。
int cols = (src.cols-1) * src.channels();//边界问题
int offsetx = src.channels();
int rows = src.rows;
Mat dst = Mat::zero(src.size(), src.type());
for(int row = 1; row < src.row - 1; row++)
{
const uchar* previous= src.ptr(row-1);
const uchar* current = src.ptr(row);
const uchar* next= src.ptr(row+1);
uchar* output = dst.ptr(row);
for(int col = offsetx; col < cols; col++)
{
output [col] = saturat_cast(5 * current [col] - (current [col - offsetx] + current [col + offsetx] + previous[col] + next[col]));//自己左右上下
}
}
double t = getTickCount();
Mat kernel = (Mat_(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
filter2D(src, dst, src.depth(), kernel);//src.depth()不知道的话就用-1;
double timeconst = (getTickCount() - t) / getTickFrequency();
printf("time consume %.2f",timeconst );
namedWindow("aaa", CV_WINDOW_AUTOSIZE);
imshow("aaa",dst)
构造函数 | 常用方法 |
Mat() | void copyTo(Mat mat) |
Mat(int rows, int cols, int type) | void convertTo(Mat dst, int type) |
Mat(Size size, int type) | Mat clone() |
Mat(int rows, int cols, int type, const Scalar &s) | int channels() |
Mat(Size size, int type, const Scalar &s) | int depth() |
Mat(int ndims, const int *sizes, int type) | bool empty() |
Mat(int ndims, const int *sizes, int type, const Scalar &s) | uchar* ptr(i=0) |
cvtColor(src, gray, CV_BGR2GRAY);
int height = gray.rows;
int width = gray.cols;
for(int row = 0; row < height; row++ )
for(int col = 0; col < width; col++)
{
int gray = gray.at(row, col);
gray.at(row, col) = 255 - gray;
}
Mat dst;
dst.creat(src.size(), src.type());
height = src.row();
width = src.clos;
int nc = src.channnels();
for(int row = 0; row < height; row++ )
for(int col = 0; col < width; col++)
{
if(nc == 1)
{
int gray = gray.at(row, col);
gray.at(row, col) = 255 - gray;
}else if(ncc==3)
{
int b = src.at(row, col)[0];
int g = src.at(row, col)[0];
int r = src.at(row, col)[0];
dst.at(row, col)[0] = 255 - b;
dst.at(row, col)[1] = 255 - g;
dst.at(row, col)[2] = 255 - r;
gray.at = max(r, max(b,g));
}
}
bitwise_not(src, dst);//范围操作
void cv::addWeighted(InputArray src1,
double alpha,
InputArray src2,
double beta,
double gamma,
OutputArray dst,
int dtype = -1
)
dst(I) = saturate(src1(I) * alpha + src2(I) * beta +gamma)
double alpha = 0.5;
if(src1.rows == src2.rows && src1.cols== src2.cols&& src1.type() == src2.type())
{
addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);
add(src1, src2, dst, Mat());
multiply(src1, src2, dst, 1.0);
}
调整亮度和对比度属于像素变换-点操作
int height = src.row();
int width = src.clos;
dst = Mat::zeros(src.size(), src.type());
float alpha = 1.2;
float beta = 30;
src.convertTo(m1,CV_32F);
int nc = src.channnels();
for(int row = 0; row < height; row++ )
for(int col = 0; col < width; col++)
{
if(nc == 1)
{
int v = m1.at(row, col);
gray.at(row, col) = sature_cast(v*alpha + beta);
}else if(nc==3)
{
float b = m1.at(row, col)[0];
float g = m1.at(row, col)[1];
float r = m1.at(row, col)[2];
dst.at(row, col)[0] = sature_cast(b*alpha + beta);
dst.at(row, col)[1] = sature_cast(g*alpha + beta);
dst.at(row, col)[2] = sature_cast(r*alpha + beta);
gray.at = max(r, max(b,g));
}
}
bitwise_not(src, dst);//范围操作
///线
Point p1 = Point(20, 30);
Point p2;
p2.x = 300;
p2.y = 300;
Scalar color = Scalar(0, 0, 255);
line(bgImage, p1, p2, color, 1, LINE_8);
///矩形
Rect rect = Rect(200, 100, 300, 300);
Scalar color = Scalar(255, 0, 0);
rectangle(bgImage, rect, color, 2, LINE_8);
///椭圆
Scalar color = Scalar(0,255,0)
ellipse(bgmimage, Point(bgImage.cols/2, bgImage.row/2),Size(bgImage.cols/4, bgImage.row/8),90,0,360,color,2,LINE_8)//背景、椭圆中心、size(长轴、短轴)、切斜度、0~360圆弧绘制角度范围、颜色、线宽、线类型。
///圆
Scalar color = Scalar(0, 255, 255);
Point center = Point(bgImage.cols / 2, bgImage.row / 2);
circle(bgImage, center, 150, color, 2, 8);
///多边形
Point pts[1][5];
pts[0][0] = Point(100 ,100);
pts[0][1] = Point(100 ,200);
pts[0][2] = Point(200 ,200);
pts[0][3] = Point(200 ,100);
pts[0][4] = Point(100 ,100);
const Point* ppts[] = {pts[0]};
int npt[] = {5};
Scalar color = Scalar(255, 12, 255);
fillPoly(bgImage,ppts,npt,1,color,8);//背景图,点坐标,点个数,轮廓数,颜色,线类型
///文字
putText(bgImage,"Hello Opencv", Point(300, 300), CV_POINT_HERSHEY_COMPLEX, 1.0, Scalar(12,255,200),1,8);//倒数第二个就是粗细调整
//绘制随机颜色
RNG rng(12345);
Point pt1;
Point pt2;
Mat bg = Mat::zeros(bgImage.size(), bgImage.type());
for(int i = 0; i < 100000; i++)
{
pt1.x = rng.uniform(0, bgImage.cols);
pt2.x = rng.uniform(0, bgImage.cols);
pt1.y = rng.uniform(0, bgImage.rows);
pt2.y = rng.uniform(0, bgImage.rows);
Scalar color = Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));
line(bgImage, p1, p2, color, 1, LINE_8);
}
blur(src, dst, Size(3,3), Point(-1, -1));//x方向,y方向
GaussianBlur(src, dst, Size(5,5), 11, 11);//x方向sigma,y方向sigma
主要是用于美容效果;
空域核:空间域
值域核:像素值域,相差大的被保留,差异保留存在。
medianBlur(src,dst,3);
bilateralFilter(src, dst, 15, 100,3);//100降低,边缘信息更多的被保留
GaussianBlur(src,dst,size(15,15),3,3);
Mat kernel = (Mat_(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
filter2D(src,dst,-1,kernel,Point(-1,-1),0);
Mat src,dst;
char OUTPUT_WIN[]="output image";
int element_size=3;
int max_size = 21;
void CallBack_Demo(int, void*);
int main(int argc, char** argv){
src = imread("ss");
if(!src.data){
printf("xx");
return -1;
}
createTrackbar("Element Size:", OUTPUT_WIN, &element_size, CallBack_Demo);
CallBack_Demo(0,0);
}
void CallBack_Demo(int, void*)
{
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s),Point(-1,-1));
dilate(src,dst,structureElement ,Point(-1,-1),1);
erode(src,dst,structureElement );
return;
}
消除图中点。
结构元素很重要。
Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));
morphologyEx(src,dst,CV_MOP_OPEN,kernel);
Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));
morphologyEx(src,dst,CV_MOP_CLOSE,kernel);
Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));
morphologyEx(src,dst,CV_MOP_GRADIENT,kernel);
Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));
morphologyEx(src,dst,CV_MOP_TOPHAT,kernel);
Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));
morphologyEx(src,dst,CV_MOP_BLACKHAT,kernel);
通过自定义的结构元素,实现结构元素对输入图像一些对象敏感,另外一些对象不敏感,这样就使得敏感的对象改变而不敏感的对象保留输出,通过基本形态学操作-膨胀与腐蚀,使用不同的结构元素实现对输入图像的操作,得到想要的结果。
src=imread();
cvtColor(src, gray,CV_BGR2GRAY);
adapativeThreshold(src,bin,255,ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY,15,-2);
Mat hline = getStructuringElement(MOTPH_RECT, Size(src.cols/16,1), Point(-1,-1));
Mat vline = getStructuringElement(MOTPH_RECT, Size(1,src.rows/16), Point(-1,-1));
erode(binImg, temp, hline);
dilate(temp, dst, hile);
morphologyEx(binImage,dst,CV_MOP_OPEN,vline );
erode(binImg, temp, vline );
dilate(temp, dst, vline );
bitwise_not(dst,dst);
blur(dst,dst,Size(3,3),Point(-1,-1));
//////////////////////
Mat kernel = getStructuringElement(MOTPH_RECT, Size(5,5), Point(-1,-1));
adaptiveThreshold(
Mat src,//输入的灰度图像
Mat dst,//二值图像
double maxValue;//二值图像的最大值
int adaptiveMethod;//自适应方法,ADAPTIBE_THRESH_MEAN_C,ADAPTIVE_THRESH_GAUSSIAN_C
int thresholdType,//阈值类型
int blockSize;//块大小
double C//常量C可以是正数,0,负数
)
pyrUp(src,dst,Size(src.cols*2,src.rows*2));
pyrDown(src,dst,Size(src.cols/2,src.rows/2));
GaussianBlur(gray, gauss1,Size(3,3),0,0);
GaussianBlur(gauss1, gauss2,Size(3,3),0,0);
subtract(gauss1,gauss1,dogImg);//低的减去高的
normalize(dogImg,dogImg,255,0,NOR_MINMAX);
#include
#include
#include
using namespace cv;
Mat src, gray_src, dst;
int threshold_value = 127;
int threshold_max = 255;
int type_value=2;
int type_max=5;
const char* output_title = "binary image";
void Threshold_Demo(int , void*);
int main(int argc, char** argv)
{
src = imread("d://");
if(!src.data)
{
printf("aaa");
return -1;
}
nameWindow("input image", CV_WINDOW_AUTOSIZE);
nameWindow(output_title, CV_WINDOW_AUTOSIZE);
imshow("input image", src);
createTrackbar("Threshold Value:", output_title, &threshold_value, threshold_max, Threshold_Demo);
createTrackbar("Type Value:", output_title, &type_value, type_max, Threshold_Demo);
Threshold_Demo(0,0);
waiteKey(0);
return 0;
}
void Threshold_Demo(int , void*)
{
cvColor(src, gray_src, CV_BGR2GRAY);
printf("%d",THRESH_BINARY);
printf("%d",THRESH_BINARY_INV);
printf("%d",CV_THRESH_TOZERO);
printf("%d",CV_THRESH_TOZERO_INV);
printf("%d",THRESH_TRUNG);
threshold(gray_src, dst, threshold_value,type_value);
threshold(gray_src, dst, 0,THRESH_OTSU|type_value);
threshold(gray_src, dst, 0,THRESH_TRUNG|type_value);
imshow(output_title, dst);
}
Robert算子
1 | 0 |
0 | -1 |
0 | 1 |
-1 | 0 |
Sobel算子
-1 | 0 | 1 |
-2 | 0 | 2 |
-1 | 0 | 1 |
-1 | -2 | -1 |
0 | 0 | 0 |
1 | 2 | 1 |
拉普拉斯算子
0 | -1 | 0 |
-1 | 4 | -1 |
0 | -1 | 0 |
//Robert X方向
Mat kernel_x = (Mat_(2,2)<<1,0,0,-1);
filter2D(src,dst,-1,kernel_x ,Point(-1,-1),0.0);
//Robert Y方向
Mat kernel_y = (Mat_(2,2)<<0,1,-1,0);
filter2D(src,dst,-1,kernel_y ,Point(-1,-1),0.0);
//Sobel X方向
Mat kernel_x = (Mat_(3,3)<<-1,0,1,-2,0,2,-1,0,1);
filter2D(src,dst,-1,kernel_x ,Point(-1,-1),0.0);
//Sobel Y方向
Mat kernel_y = (Mat_(3,3)<<-1,-2,-1,0,0,0,1,2,1);
filter2D(src,dst,-1,kernel_y ,Point(-1,-1),0.0);
//laplace算子是整个差异
Mat kernel_y = (Mat_(3,3)<<0,-1,0,-1,4,-1,0,-1,1);
filter2D(src,dst,-1,kernel_y ,Point(-1,-1),0.0);
filter2D
{
Mat src,//输入图像
Mat dst;//模糊图像
int depth;//图像深度32/8
Mat kernel;//卷积核/模板
Point anchor,//锚点位置
double delta//计算出来的像素+delta
}
int c = 0;
int index = 0;
int ksize = 3;
while(true){
c = waitKey(500);
if((char)c == 27){
break;
}
ksize = 4 + (index % 5) *2 +1;
Mat kernel = Mat::ones(Size(ksize , ksize ),CV_32F)/(float)(ksize ,ksize );
filter2D(src, dst, -1,kernel,Point(-1,-1));
index++;
}
在卷积开始之前增加边缘像素,填充值为0或者RGB,在卷积处理之后再去掉这些边缘,默认的处理方法是BORDER_DEFAULT,此外还有以下:
copyMakeBorder(
-Mat src,//输入图像
-Mat dst,//添加边缘图像
-int top,//边缘长度,一般上下左右都取相同值
-int bottom,
-int left,
-int right,
-int borderType,//边缘类型
-Scalar value//颜色
)
int top=(int)(0.05*src.rows);
int bottom=(int)(0.05*src.rows);
int left=(int)(0.05*src.cols);
int right=(int)(0.05*src.cols);
RNG rng(12345);
int borderType = BORDER_DEFAULT;
int c = 0;
while(true){
c = waitKey(500);
if( (char)c == 27){//ESC
break;
}
if((char)c=='r'){
borderType = BORDER_REPLICATE;
}else if((char )c=='v'){
borderType = BORDER_VRAP;
}else if((char )c=='C'){
borderType = BORDER_CONSTANT;
}else{
borderType = BORDER_DEFAULT;
}
Scalar color = Scalar(rng.unifor(0,255),rng.unifor(0,255),rng.unifor(0,255));
copyMakeBorder(src,dst,top,bottom,left,right,borderType ,color);
}
///
GaussianBlur(src, dst, Size(5,5),0,0,BORDER_DEFAULT);
图形变化比较大,求导数,最高点就是变化最大的地方。
cv::Sobel(
InputArray Src,//输入图像
OutputArray dst;//输出图像,大小与输入图像一直
int depth,//输出图像深度
Int dx,//x方向,几阶导数
Int dy,//y方向,几阶导数
Int ksize,//SOBEL算子kernel大小,必须是1、3、5、7
double scale =1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
cv::Scharr(
InputArray Src,//输入图像
OutputArray dst;//输出图像,大小与输入图像一直
int depth,//输出图像深度
Int dx,//x方向,几阶导数
Int dy,//y方向,几阶导数
double scale =1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
GuassianBlur(src,dst,Size(3,3),0,0);
cvtColor(dst,gray_src,CV_BGR2GRAY);
Mat xgrad,ygrad;
Sobel(gray_src,xgrad,CV_16S,1,0,3);//1:x方向1阶导数,0:y方向0阶导数
Sobel(gray_src,ygrad,CV_16S,0,1,3);
convertScaleAbs(xgrad,xgrad);
convertScaleAbs(ygrad,ygrad);
//
//Sobel(gray_src,xgrad,-1,1,0,3);//细节信息会丢失,特别是在直接做截断处理的时候
//Sobel(gray_src,ygrad,-1,0,1,3);
addWeighted(xgrad,0.5,ygrad,0.5,0,xygrad);
int height = src.row();
int width = src.clos;
Mat xygrad = Mat(xgrad.size(),xgrad.type());
for(int row = 0; row < height; row++ )
for(int col = 0; col < width; col++)
{
int xg= xgrad.at(row, col);
int yg= ygrad.at(row, col);
int xyg = xg + yg;
xgrad.at(row, col) = saturate_cast(xyg);
}
bitwise_not(src, dst);//范围操作
Scharr(gray_src,xgrad,CV_16S,1,0);//输出位深度要比输入的好,对于边缘干扰有较好的鲁棒性。
Scharr(gray_src,ygrad,CV_16S,0,1);
在二阶导数的时候,最大变换处的值为零,即边缘是0,通过二阶倒数计算,根据此理论可以计算图像的二阶倒数,提取边缘。
src=imread("");
if(!src.data){
printf("");
}
GaussianBlur(src,dst,Size(3,3),0,0);
cvtColor(dst,gray_src,CV_BGR2GRAY);
Laplacian(gray_src,edge_image,CV_16S,3);
convertScaleAbs(edge_image,edge_image);
threshold(edge_image,edge_image,0,255,THRESH_OTSU| THRESH_BINARY)
canny(
InputArray src,//8-bit输入图像一定要是8位的
OutputArray edges,//输出边缘图像,一般都是二值化图像,背景是黑色
double threshold1,//低阈值,通常为高阈值的1/2或者1/3
double threshold2,//高阈值
int aptertureSize,//Sobel 算子的size,一般的都是3x3,取值3
bool L2gradient//选择ture表示L2来归一化(平方和开根号),否则选择L1归一化(绝对值相加)
)
int t1_value = 50;
int max_value = 255;
const char* OUTPUT_TITLE ="ddd";
void Canny_Demo(int ,void*);
Mat gray_src;
cvtColor(src,gray_src,CV_BGR2GRAY);
CreateTrackbar("sss",OUTPUT_TITLE ,&t1_value ,max_value ,Canny_Demo);
Canny_Demo(0,0);
void Canny_Demo(int ,void*)
{
Mat edge_output;
blur(gray_src,gray_src,Size(3,3),Point(-1,-1),BORDER_DEFAULT);
Canny(gray_src,edge_output,t1_value*2,3,false);
dst.creat(src.size(),src.type());
Mat mask1 = Mat::zeros(src.size(),src.type());
invert(edge_output,edge_output);
src.copyTo(dst,edge_output);
}
平面空间到霍夫空间
在x0&y0不变的情况下,每给定一个\theta就内确定一个r_\theta。
\theta有[0,360°],绘制曲线。
如果在x1,y1也绘制一条这样的曲线,发现多条曲线交于一点,标明这些点构成一条直线。
根据r和\theta获得x,y。
在霍夫空间中,如果每相交一次,灰度值加1,则在霍夫空间中相交的部分为亮的部分。
平面坐标->霍夫空间(极坐标)->平面坐标 :极坐标图很亮的点
cv::HoughLines(
InputArray src,//输入图像,必须8-bit的灰度图像
OutputArray lines,//输出极坐标来表示直线
double rho,//生成极坐标是像素扫描的步长
double theta,//生成极坐标时的角度步长,一般取值CV_PI/180
int threshold,//阈值,只有获得足够交点的极坐标才能被看成是直线,
double srn=0;//是否应用多尺度的霍夫变换,如果不是设置为0表示经典霍夫变换
double stn = 0;//是否应用多尺度的霍夫变换,如果不是设置为0表示经典霍夫变换
double min_theta = 0;//表示角度扫描范围0~180之间默认即可
double max_theta=CV_PI
)
cv::HoughLinesP(
InputArray src,//输入图像,必须8-bit的灰度图像
OutputArray lines,//输出极坐标来表示直线
double rho,//生成极坐标是像素扫描的步长
double theta,//生成极坐标时的角度步长,一般取值CV_PI/180
int threshold,//阈值,只有获得足够交点的极坐标才能被看成是直线,
double minLineLenght= 0,//最小直线长度
double maxLineGap=0//最大间隔
)
Canny(src,src_gray,100,200);//双阈值
cvtColor(src_gray,dst,CV_GRAY2BGR);
vector plines;
HoughLinesP(src_gray,pline,1,CV_PI / 180.0, 10,0,10);
Scalar color = Scalar(0,0,255);
for(size_t i = 0; i
圆最重要的是圆心坐标以及半径。
转变成极坐标半径固定,\theta变换[0,360°]变换,生成一个虚线的圆,多个圆有一个交点,霍夫空间累加,霍夫空间最亮,相交的圆个数最多,为圆心。
每给定一个R就有一个圆,笛卡尔的圆上所有点在极坐标上做圆都会相交于圆心。
HoughCircles{
InputArray image,//输入图像,必须是8位的单通道灰度图像。
OutputArray circle,//输出结果发现是圆信息,向量信息
Int method,//方法-HOUGH_GRADIENT,通过梯度寻找
Double dp,//dp=1;尺度,是否在在原图寻找(1),比原图小一半(2)
Double mindist,//10最短距离,两个圆心多远,可以分辨是两个圆的,否则认为是同心圆(半径不一样),src_gray.rows/8
Double param1,//canny edge detection lowthreshold,第一步canny求梯度
Double param2,//中心点累加器阈值-候选圆心,多长的圆弧定义为圆,越大越慢,(从小到大寻找圆心)
Int minradius,//最小半径
Int maxradius//最大半径
}
medianBlur(src,moutput,3);//去噪
cvtColor(moutput,moutput,CV_BGR2GRAY);
//霍夫检测
vector pcircles;
// 输入,输出,梯度检测,放缩倍率,同心圆最小圆心距,canny检测低阈值,检测为圆的最小点数,最小半径,最大半径
HoughCircles(moutput,pcircles,CV_HOUGH_GRADIENT,1,10,100,30,5,50);
src.copyTo(dst);
for(size_t i-0; i
镜像:
Remap{
InputArray src,//输入图像
OutputArray dst,//输出图像
InputArray map1,//x映射表CV_32FC1/CV_32FC2
InputArray map2;//y映射表
int interpolation,//选择插值方法,常见线性差值,可选择立方等
int borderMode,//BORDER_CONSTANT,边缘填充方式
const Scalar borderValue//color
}
map_x.create(src.size(),CV_32F);
map_y.create(src.size(),CV_32F);
while(true)
{
c=waiteKey(500);
if((char)c==27)
{
break;
}
index=c%4;
update_map();
remap(src,dst, map_x,map_y,INTER_LINEAP,BORDER_CONSTANT,Scalar(0,255,255));
}
void update_map(void)
{
for(int row=0; row (src.cols * 0.25) && col<=(src.cols*0.75)&& row>(src.rows*0.25)&&row<=(src.rows*0.75))//差值的形式缩小
{
map_x.at(row,col)=2*(col - (src.cols*0.25)+0.5);//多出像素会出问题
map_y.at(row,col)=2*(row - (src.rows*0.25)+0.5);
}
else
{
map_x.at(row,col)=0;
map_y.at(row,col)=0;
}
break;
case 1://水平镜像
map_x.at(row,col)=(src.cols-col-1);
map_y.at(row,col)=row;
case 2://垂直镜像
map_x.at(row,col)=col;
map_y.at(row,col)=(src.rows-row-1);
case 3://
map_x.at(row,col)=(src.cols-col-1);
map_y.at(row,col)=(src.rows-row-1);
}
}
}
也就是该灰度值出现的频率,梯度也可以做直方图,统计数值在一定范围内的都可以。例如梯度直方图、方向直方图。
是一种提高图像对比度的方法,拉伸图像灰度值范围。
从一个灰度值分布映射到另一个灰度值分布,也是一个重映射remap
equalizeHist(InputArray src, OutputArray dst)//驶入必须是8位单通道。
cvtColor(src,src,CV_BGR2GRAY)
equalizeHist(src,dst)
统计得到各个灰度等级出现的次数,每个等级绘制的次数叫做BIN
-dim表示维度,对灰度直方图来说只有一个通道值dims=1
-bins表示在维度中子区域大小划分,bins=256,划分为256个级别
-range表示值的范围,灰度值范围为[0,255]
split(//将多通道的图像拆分为多个单通道的图像。
const Mat &src,//输入图像
Mat* mvbegin)//输出的多通道图像数组
calcHist(//输出统计结果
const Mat* images,//输入图像指针
int images,//图像个数
const int* channels,//通道数目
InputArray mask,//输入mask,可选
OutputArray hist,//输出直方图数据
int dims,//维数
const int *histsize,//直方图级数
const float * ranges,//值域范围
bool uniform,//true默认为真每一个竖条的宽度是否相等。
bool accumulate//默认false是否累加。如果为true,在下次计算的时候不会首先清空hist。
)
vectorbgr_planes;
split(src,bgr_planes);
imshow("ddd",bgr_planes[0]);
//计算直方图
int histSize = 256;
float range[] = {0,255}
float *histRanges = {range};
Mat b_hist,g_hist,r_hist;
//输入,图像数,通道数,掩码,统计后的直方图,输出的直方图维度,每个维度上直方图统计个数,进行统计的范围,直方图柱状统计条之间是否等间距,是否累加
calcHist(&bgr_planes[0],1,0,Mat(),b_hist,1,&histSize,&histRanges,ture,false);
calcHist(&bgr_planes[1],1,0,Mat(),g_hist,1,&histSize,&histRanges,ture,false);
calcHist(&bgr_planes[2],1,0,Mat(),r_hist,1,&histSize,&histRanges,ture,false);
int hist_h = 400;
int hist_w = 512;
int bin_w =hist_w/histSize;
Mat histImage(hist_w,hist_h,CV_8UC3,Scalar(0,0,0));
//归一化直方图
normalize(b_hist,b_hist,0,hist_h , NORM_MINMAX,-1,Mat());
normalize(g_hist,g_hist,0,hist_h , NORM_MINMAX,-1,Mat());
normalize(r_hist,r_hist,0,hist_h , NORM_MINMAX,-1,Mat());
//绘制直方图
for(int i=0;i(i-1))),Point((i-1)*bin_w,hist_h -cvRound(b_hist.at(i))),Scalar(255,0,0),2,LINE_AA);
line(histImage,Point((i-1)*bin_w,hist_h -cvRound(g_hist.at(i-1))),Point((i-1)*bin_w,hist_h -cvRound(g_hist.at(i))),Scalar(0,255,0),2,LINE_AA);
line(histImage,Point((i-1)*bin_w,hist_h -cvRound(r_hist.at(i-1))),Point((i-1)*bin_w,hist_h -cvRound(r_hist.at(i))),Scalar(0,0,255),2,LINE_AA);
}
直方图之间有相似性,衡量两张图像或者区域中间相关性。
对输入的两张图像计算得到直方图H1、H2,归一化当相同的尺度空间,然后通过计算H1、H2之间的距离得到两个直方图的相似程度,今儿比较两张图像本身的相似程度,Opencv一共有四种方法:
-Correlation 相关性比较
-Chi-Square卡方比较
-Intersection十字交叉性
-Bhattacharry distance巴氏距离
其中N是直方图BIN个数,取值范围要一致,是均值。也就是统计学里面的相关性系数,H1和H2一样的话就是1
统计学里面的卡方验证,查看两个数据分布的相关性。
、分别表示两个图像的直方图数据
、分别表示两个图像的直方图数据
是均值,1最不相似,0完全一致
、分别表示两个图像的直方图数据
compareHist(
InputArray h1,//直方图数据,下同
InputArray h2,
int method//比较方法,上述四种方法之一
)
cvtColor(base,base,CV_BGR2HSV);
cvtColor(test1,test1,CV_BGR2HSV);
cvtColor(test2,test2,CV_BGR2HSV);
int h_bins=50;int s_bins=60;
int binSize[]={h_bins,s_bins};
//hue varies from 0 to 179, saturation from 0 to 255
float h_ranges[] = {0,180};
float s_ranges[] = {h_bins, s_bins};
const float* ranges[]={h_ranges,s_ranges};
//use the 0-th and 1-st channels
int channels[]={0,1};
MatND hist_base;
MatND hist_test1;
MatND hist_test2;
calcHist(&base,1,channels,Mat(),hist_base,2,histSize,ranges,true,false);
normalize(hist_base,hist_base,0,1,NORM_MINMAX,-1,Mat())
calcHist(&test1,1,channels,Mat(),hist_test1,2,histSize,ranges,true,false);
normalize(hist_test1,hist_test1,0,1,NORM_MINMAX,-1,Mat())
calcHist(&test2,1,channels,Mat(),hist_test2,2,histSize,ranges,true,false);
normalize(hist_test2,hist_test2,0,1,NORM_MINMAX,-1,Mat())
double basebase=compareHist(hist_base,hist_base,CV_COMP_CORREL);
double basetest1=compareHist(hist_base,hist_test1,CV_COMP_CORREL);
double test1test2=compareHist(hist_test1,hist_test2,CV_COMP_CORREL);
printf("test1 compare with test2 correlation value :%f",test1test2);
putText(base,convertToString(basebase),Point(50,50),CV_FONT_HERSHEY_COMPLEX,1,Scalar(0,0,255),2,LINE_AA);
string convertToString(double d){
ostringstream os;
if(os<
cvtColor(src,hsv,CV_BGR2HSV);
hue.creat(hsv.size(),hsv.depth());
int nchannels[] = {0,0};
//将指定通道的数据复制过去
mixChannels(&hsv,1,&hue,1,nchannels,1);
createTrackbar("Histogram Bins:",window_image,&bins,180,Hist_And_Backprojection);
Hist_And_Backprojection(0,0);
void Hist_And_Backprojection(int,void)
{
float range[]={0,180};
const float *histRanges = {range};
Mat h_hist;
calcHist(hue,1,0,Mat(),h_hist,&bins,&histRanges,true,flase);
normalize(h_hist,h_hist,0,1,NORM_MINMAX,-1,Mat());
Mat backPrjImage;
calcBackProject(&hue,1,0,h_hist,backPrjImage,1,true);
int hist_h=400;
int hist_v=400;
Mat histImage;
histImage.create(hist_v,hist_v,CV_8UC3,Scalar(0,0,0));
int bin_w =(hist_w/bins);
for(int i=0; i (i)*(400/255)))),Point((i)*bin_w,hist_h-cvRound(h_hist.at(i)*(400/255)))),Scalar(0,0,255),-1));
}
}
模板也是一副图像(小图形),在大图像中找到相似的图像,就是模板匹配。
Opencv提供六钟常见的匹配算法,如下:
例如计算平方不同,前者为模板,后者覆盖在原图上。两个像素值相减后平方,
matchTemplate(
InputArray image;//源图像,必须是8-bit或者32-bit浮点数图像。
InputArray template;//模板图像,类型与输入图像一致
OutputArray result;//输出结果,必须是单通道32位浮点数,假设原图像W*H模板图像w*h,
int method;//使用匹配方法,推荐使用归一化方法
InputArray mask=noArray();//(optional)
)
enum cv::TemplateMatchModes{
cv::TM_SQDIFF = 0,
cv::TM_SQDIFF_NORMED = 1,
cv::TM_CCORR = 2,
cv::TM_CCORR_NORMED = 3,
cv::TM_CCOEFF = 4,
cv::TM_CCOEFF_NORMED=5
}
Mat src, temp, dst;
int match_method = CV_TM_SQDIFF;
int max_track = 5;
const char* INPUT_T = "input image";
const char* OUTPUT_T = "result image";
const char* match_t = "template match-demo";
src=
temp=
namedWindow(INPUT_T , CV_WINDOW_AUTOSIZE);
const char* trackbar_title="Match Algo Type";
createTrackbar(trackbar_title,OUTPUT_T ,&match_method ,max_track,Match_Demo);
atch_Demo(0,0);
void Match_Demo(int ,void*){
Mat img_display;
src.copyTo(img_display);
int result_rows = src.rows - temp.rows + 1;//边界
int result_cols = src.cols - temp.cols + 1;
dst.create(Size(result_cols, result_rows),CV_32FC1);//
matchTemplate(src, temp, dst, match_method);
normalize(dst, 0, 1, NORM_MINMAX, -1, Mat());//启用归一化
double minValue, maxValue;
Point minLoc;
Point maxLoc;
Point matchLoc;
minMaxLoc(dst, &minValue, &maxValue, &minLoc, &maxLoc, Mat());//找到最小最大值位置
if(match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED){
matchLoc = minLoc;
}
else{
matchLoc = maxLoc;
}
//绘制矩形
//rectangle(img_display, matchLoc, Point(matchLoc.x + temp.cols, matchLoc.y + temp.rows), Scalar::all(0), 2, LINE_AA);
//rectangle(dst, matchLoc, Point(matchLoc.x + temp.cols, matchLoc.y + temp.rows), Scalar::all(0), 2, LINE_AA);
rectangle(dst, Rect(temLoc.x, temLoc.y,temp.cols,temp.rows),Scalar(0, 0, 255), 2, 8);
rectangle(result, Rect(temLoc.x, temLoc.y,temp.cols,temp.rows),Scalar(0, 0, 255), 2, 8);
imshow(OUTPUT_T, dst);
imshow(match_t, img_display);
return;
}
findContours发现轮廓
drawContours绘制轮廓
canny阈值有高阈值和低阈值,低阈值选取后,高阈值一般是它的1.5、2、3倍。
在二值图像上发现轮廓使用API cv::findContours(
InputOutputArray binImg,//输入图像,非0的像素被看成1,0的像素保持不变,8-bit
OutputArrayOfArrays contours,//全部发现的轮廓对象
OutputArray hierachy,//该图的拓扑结构,可选,该轮廓发现算法正是基于图像拓扑结构实现的
int mode,//轮廓返回的模式
int method,//发现方法
Point offset=Point()//轮廓像素的位移,默认(0,0)没有位移
)
在二值图像上发现轮廓使用API cv::findContours之后对发现的轮廓数据进行绘制显示
drawContours(
InputOutputArray binImg,//输出图像
OutputArrayOfArrays contours,//全部发现的轮廓对象
int contourIdx,//轮廓索引号
const Scalar &color,//绘制时候颜色
int thickness,//绘制线宽度
int lineType,//线的类型LINE_8
InputArray hierarchy,//拓扑结构图
int maxlevel,//最大层数,0之绘制当前的,1表示绘制当前及其内嵌轮廓
Point offset=Point()//轮廓位移,可选
)
int threshold_value=100;
int threshold_max =255;
const char* output_win="contours";
namedWindow(output_win,CV_WINDOW_AUTOSIZE);
cvtColor(src,src,CV_BGR2GRAY);
const char* track_title="Threshold Value";
createTrackbar(track_title,output_win,&threshold_value,threshold_max,Demo_Contours);
Demo_Contours(0,0);
void Demo_Contours(int ,void){
Mat canny_output;
vector> points;
vector hierachy;
Canny(src,canny_output,threshold_value,threshold_value*2,3,false);
findContours(canny_output,points,hierachy,RETR_TREE, CHAIN_APPORX_SIMPLE,Point(0,0));
dst = Mat::zeros(src.size(),cv_8UC3);
RNG rng;
for(size_t i=0;i
convexHull(
InputArray points,//输入候选点,来自findContours
OutputArray hull,//凸包
bool clockwise,//default true,顺时针方向
bool returnPoints//true 表示返回点个数,如果第二个参数是vector
)
int threshold_value=100;
int threshold_max =255;
const char* output_win="contours";
namedWindow(output_win,CV_WINDOW_AUTOSIZE);
cvtColor(src,src_gray,CV_BGR2GRAY);
blur(src_gray,src_gray,Size(3,3),Point(-1,-1),BORDE_DEFAULT);
const char* trackbar_label="Threshold :";
createTrackbar(trackbar_label,output_win,&threshold_value,threshold_max,Threshold_Callback);
Threshold_Callback(0,0);
void Threshold_Callback(int ,void*){
Mat bin_output;
vector> contours;
vector hierachy;
threshold(src_gray,bin_output,threshold_value,threshold_max,THRESH_BINARY);
findContours(bin_output,contours,hierachy,RETR_EXTERNAL, CHAIN_APPORX_SIMPLE,Point(0,0));
vector> convexs(contours.size());
dst = Mat::zeros(src.size(),cv_8UC3);
RNG rng;
for(size_t i=0;i empty;
drawContours(dst,convexs,i,color,2,8,empty,0,Point(0,0));
}
inshow(output_win,dst);
}
approxPloyDP(InputArray curve, OutputArray approxCurve, double epslion, bool closed)
基于RDP算法实现,目的是减少多边形轮廓点数
Point2f& center,//圆心位置
float& radius)//圆的半径
Mat src, gray_src,drawImg;
int threshold_value=100;
int threshold_max =255;
const char* output_win="contours";
namedWindow(output_win,CV_WINDOW_AUTOSIZE);
cvtColor(src,src_gray,CV_BGR2GRAY);
blur(src_gray,src_gray,Size(3,3),Point(-1,-1),BORDE_DEFAULT);
const char* trackbar_label="Threshold :";
createTrackbar(trackbar_label,output_win,&threshold_value,threshold_max,Threshold_Callback);
Threshold_Callback(0,0);
void Contours_Callback(int ,void*){
Mat bin_output;
vector> contours;
vector hierachy;
threshold(gray_src,binary_output,threshold_value,threshold_max,THRESH_BINARY);
findContours(binary_output,contours,hierachy,RETR_TREE, CHAIN_APPORX_SIMPLE,Point(0,0));
vector> countours_play(contours.size);
vector ploy_rects(contours.size);
vector hierachy(contours.size);
vector ccs(contours.size);
vector radius(contours.size);
vector minRects;
vector myellipse;
dst = Mat::zeros(src.size(),cv_8UC3);
RNG rng;
for(size_t i=0;i5)
{
myellipse[i] = fitEllips(contours_ploy[i]);
minRecte[i] = minAreaRect(contours_ploy[i]);
}
src.copyTo(drawImg);
Point2f pts[4];
for(size_t t=0; t
几何矩
图像中心Center(x0,y0)
moments(
InputArray array,//输入数据
bool binaryImage=false//是否为二值图像
)
contoursArea(
InputArray contours,//输入轮廓数据
bool oriented//默认false,返回绝对值
)
arcLength(
InputArray curve,//输入曲线数据
bool closed//是否是封闭曲线
)
Mat src, gray_src,drawImg;
int threshold_value=100;
int threshold_max =255;
const char* output_win="contours";
namedWindow(output_win,CV_WINDOW_AUTOSIZE);
cvtColor(src,gray_src,CV_BGR2GRAY);
GaussianBlur(gray_src.gray_src,Size(3,3),0,0)
const char* trackbar_label="Threshold :";
createTrackbar(trackbar_label,output_win,&threshold_value,threshold_max,Demo_Moments);
Demo_Moments(0,0);
void Demo_Moments(int ,void*){
Mat binary_output,canny_output;
vector> contours;
vector hierachy;
Canny(gray_src,canny_output,threshold_value,threshold_value+2,3,false);
findContours(canny_output,contours,hierachy,RETR_TREE, CHAIN_APPORX_SIMPLE,Point(0,0));
vector> countours_moments(contours.size());
vector ccs(contours.size());
for(size_t i=0;i(contours_moment[i].m10/contours_moment[i].m00),static_cast(contours_moment[i].m01/contours_moment[i].m00));
}
Mat drawImg=Mat::zeros(src.size(),CV_8UC3);
src.copyTo(drawImg);
for(size_t i=0;i
-测试一个点是否在给定多边形的内部。
pointPolygonTest(
InputArray contour,//输入的轮廓
Point2f pt,//测试点
bool measureDis//是否返回距离值,如果是false,1表示内面,0表示在边界,-1表示在外部,true返回实际距离
)
返回数据时double类型
const int r = 100;
Mat src = Mat::zeros(r*4,r*4,CV_8UC1);
vector vert(6);
vert[0] = Point(3 * r / 2, static_cast(1.34*r));
vert[1] = Point(1 * r, 2 * r);
vert[2] = Point(3 * r / 2, static_cast(2.866*r));
vert[3] = Point(5 * r / 2, static_cast(2.866*r));
vert[4] = Point(3 * r / 2, 2 * r );
vert[5] = Point(5 * r, static_cast(1.34*r));
for(int i = 0; i < 6; i++)
{
line(src, vert[i], vert[(i +1 ) % 6],Scalar(255),5,8,0);
}
vector contours;
vector hierachy;
Mat csrc;
src.copyTo(csrc);
findContours(csr,contours,hierachy,RETR_TREE,CHAIN_APPROXE_SIMPLE,Point(0,0));
Mat raw_dist = Mat::zeros(csrc.size(),CV_32FC1);
for(int row = 0 ;row (col),static_cast(row)),true);
raw_dist.at(row,col) = static_cast(row)
}
}
double minValue, maxValue;
minMaxLoc(raw_dist,&minValue,&maxValue,0,0,Mat());
Mat drawImg = Mat::zeros(src.size(),CV_8UC3);
for(int row = 0 ;row (row,col);
if(dist > 0)
{
drawImg.at(row,col)[0]=(uchar)(abs((1.0-dist/maxValue))*255);
}
else if(dist <0){
drawImg.at(row,col)[2]=(uchar)(abs(dist/minValue)*255);
}else{
drawImg.at(row,col)[0]=(uchar)(abs(255-dist));
drawImg.at(row,col)[1]=(uchar)abs(255-dist));
drawImg.at(row,col)[2]=(uchar)abs(255-dist));
}
double dist = pointPolygonTest(contours[0], Point2f(static_cast(col),static_cast(row)),true);
raw_dist.at(row,col) = static_cast(row)
}
}
const char* output_win = "point ploygon test demo";
char input_win[]="input image";
namedWindow(input_win, CV_WINDOW_AUTOSIZE);
namedWindow(output_win, CV_WINDOW_AUTOSIZE);
imshow(input_win,src);
waitKey(0);
每个山都有高低(灰度值),水位逐渐降低,水流线会进行分割。
距离变换常见算法有两种:
-不断膨胀/腐蚀得到
-基于倒角距离
分水岭变换常见算法:
-基于浸泡理论实现
cv::distanceTransform(InputArray src, OutputArray dst, OutputArray labels, int distanceType, int maskSize, int labelType = DIST_LABEL_CCOMP)
distanceType = DIST_L1/DIST_L2,
maskSize = 3x3,最新的支持5x5,推荐3x3,腐蚀结构元素
labels离散维诺图输出
dst输出8位或者32位浮点数,单一通道,大小与输入图像一致
cv::watershed(InputArray image, InputOutputArray markers);
for(int row = 0 ; row (row,col)==Vec3b(255,255,255)){
src.at(row,col)[0] = 0 ;
src.at(row,col)[1] = 0 ;
src.at(row,col)[2] = 0 ;
}
}
//sharpes
Mat kernel = (Mat_ (3,3)<<1,1,1,1,-8,1,1,1,1);
Mat imgLaplace;
Mat sharpenImg = src;
filter2D(src, imgLaplance, CV_32F, kernel,
src.convertTo(sharpenImg,CV_32F);
Point(-1,-1),0,BORDER_DEFAULT);
Mat resultImg = sharpenImg - imgLaplance;
resultImg.convertTo(resultImg,CV_8UC3);
imgLaplance.convertTo(imgLaplance,CV_8UC3);
//convert to binary
Mat binaryImg;
cvtColor(resultImg, resultImg,CV_BGR2GRAY);
threshold(resultImg,binaryImg,40,255,THRESH_BINARY | THRESH_OTSU);
imshow("binary image",binaryImg);
Mat distImg;
distanceTransform(binaryImg,distImg,DIST_L1,3,5);
normalize(distImg,distImg,0,1,NORM_MINMAX);
//binary again
threshold(distImg,distImg,.4,1,TRESH_BINARY);
//
Mat k1= Mat::ones(3,3,CV_8UC1);
erode(distImg, distImg,k1,Point(-1,-1));
//markers
Mat dist_8u;
distImg.convertTo(dist_8u,CV_8U);
vector> contours;
findContours(dist_8u,contours,RETR_EXTRENAL,CHAIN_APPROX_SIMPLE,Point(0,0));
//creat makers
Mat markers = Mat::zeros(src.size(),CV_32SC1);
for(size_t t=0;t(i) , Scalar::all(static_cast(i)+1));
}
circle(markers,Point(5,5),3,Scalar(255,255,255),-1);
//perform watershed
watershed(src,markers);
Mat mark = Mat::zeros(markers.size(),CV_8UC1);
markers.convertTo(mark,CV_8UC1);
bitwise_not(mark,mark,Mat());
//generate random color
vector colors;
for(size_t i = 0; i(row,col);
if(index > 0 &&index <= static_cast(contours.size())){
dst.at(row,col) = colors[index - 1];
}else{
dst.at(row,col) = Vec3b(0,0,0);
}
}
}