基本知识:有5种颜色模型,第一种是最常见的RGB模型,就是我们通常使用的红绿蓝三色素,同过不同比例的混合显现出不同色彩。第二种YUV颜色模型,一般是电视信号系统采取的颜色编码,Y表示像素亮度,U表示红色与亮度信号差值,V表示蓝色与亮度差值。第三种是HSV颜色模型,H是色度,S是饱和度,V是亮度。第四种是Lab颜色模型,L表示亮度,a和b是两个颜色通道,取值范围是-128到127,其中a通道由小到大颜色从绿变红,b通道有小到大颜色从蓝变黄。第五种是GRAY颜色模型。GRAY模型就是我们常说的灰度图像模型。取值范围0到255,颜色从黑变白。
在opencv中,颜色模型的转换很简单,通过cvtColor函数就能实现颜色模型转换。
# include
#include
using namespace std;
using namespace cv;
int main() {
/*颜色模型转换*/
//声明
Mat yuv_i, hsv_i, lab_i, gray_i;
//读取图片
Mat im = imread("E:\\opencv_study\\opencv_study.png");
//颜色模型转换
cvtColor(im, yuv_i,COLOR_RGB2YUV);
cvtColor(im, hsv_i, COLOR_RGB2HSV);
cvtColor(im, lab_i, COLOR_RGB2Lab);
cvtColor(im, gray_i, COLOR_RGB2GRAY);
//显示图片
imshow("原图", im);
imshow("yuv", yuv_i);
imshow("hsv", hsv_i);
imshow("lab", lab_i);
imshow("gray", gray_i);
waitKey(0);
destroyAllWindows();
return 0;
}
在opencv中,通过split函数分离多通道,通过merge函数合并多通道。
split(in,out);in是指待分离多通道图像,out是数组或者容器的mat类。
merge同理,与split不同的是,merge用数组时候需要通道数作为参数,如merge(in,3,out).而容器不用。
# include
#include
using namespace std;
using namespace cv;
int main() {
/*多通道分离与合并*/
//图像读取
Mat im = imread("E:\\opencv_study\\opencv_study.png");
//多通道分离
vector<Mat> images;
Mat im0,im1,im2;
split(im, images);
im0 = images.at(0);
im1 = images.at(1);
im2 = images.at(2);
imshow("R通道", im0);
imshow("G通道", im1);
imshow("B通道", im2);
//多通道合并
cout << "图片大小:" << images[1].size() << endl;
images[1] = Mat::zeros(images[1].size(), CV_8UC1);
Mat result;
merge(images,result);
imshow("缺少G通道", result);
waitKey(0);
destroyAllWindows();
return 0;
}
最大值与最小值:minMaxLoc(im, &minv, &maxv, &minid, &maxid);
im为单通道矩阵。minv,maxv分别为通道的最大值与最小值。minid与maxid分别为矩阵中的位置,为point类型。
平均值与标准差计算:meanStdDev(im,my_mean,my_std);
im为图像,my_mean是平均值,my_std为标准差;
# include
#include
using namespace std;
using namespace cv;
int main() {
Mat im = imread("E:\\opencv_study\\opencv_study.png");
vector<Mat> images;
Mat im0, im1, im2;
split(im, images);
im0 = images.at(0);
im1 = images.at(1);
im2 = images.at(2);
double minv, maxv;
Point minid, maxid;
minMaxLoc(im0, &minv, &maxv, &minid, &maxid);
cout << "最大值:" << maxv << "---位置:" << maxid << endl;
cout << "最小值:" << minv << "---位置:" << minid << endl;
Mat my_mean,my_std;
meanStdDev(im,my_mean,my_std);
cout << "平均值:" << my_mean << "---标准差:" << my_std<<endl;
return 0;
}
两图像对应较小像素得到的图像:min(im1,im2,min_im),min_im是得到的图像。
两图像对应较大像素得到的图像:min(im1,im2,max_im),max_im是得到的图像。
# include
#include
using namespace std;
using namespace cv;
int main() {
//读取图片
Mat im = imread("E:\\opencv_study\\opencv_study.png");
//得到图片长宽
int w=im.size().width;
int h = im.size().height;
//裁剪成两幅一样大的图片
Mat im1, im2;
Rect rect1(0, 0, w / 2, h);
Rect rect2(w/2, 0, w / 2, h);
im1 = im(rect1);
im2 = im(rect2);
//比较对应像素大小获得对应像素
Mat min_im,max_im;
min(im1, im2, min_im);
max(im1, im2, max_im);
imshow("min", min_im);
imshow("max", max_im);
waitKey(0);
destroyAllWindows();
}
运行得:
两幅图像的逻辑操作:
进行的逻辑运算是从十进制转换成二进制进行。
# include
#include
using namespace std;
using namespace cv;
int main() {
/*图像逻辑运算*/
Mat im = imread("E:\\opencv_study\\opencv_study.png");
Mat my_or, my_and, my_xor, my_not;
bitwise_and(im, im, my_and);
bitwise_or(im, im, my_or);
bitwise_xor(im, im, my_xor);
bitwise_not(im, my_not);
imshow("and", my_and);
imshow("or", my_or);
imshow("xor", my_xor);
imshow("not", my_not);
waitKey(0);
destroyAllWindows();
}
原理:比较简单,通过设置阈值得到二值图像。
# include
#include
using namespace std;
using namespace cv;
int main() {
Mat im = imread("E:\\opencv_study\\opencv_study.png");
Mat im_b, im_bv;
threshold(im, im_b, 125, 255, THRESH_BINARY);
threshold(im, im_bv, 125, 255, THRESH_BINARY_INV);
imshow("im_b", im_b);
imshow("im_bv", im_bv);
waitKey(0);
destroyAllWindows();
return 0;
}
图像连接就是降两张具有相同高度或者相同宽度的图像连接起来。
opencv4中提供了两个函数用于图像连接,vconcat()函数用于实现图像的上下连接,hconcat()函数用于实现图像的左右连接。
# include
#include
using namespace std;
using namespace cv;
int main() {
//读取图片
Mat im = imread("E:\\opencv_study\\opencv_study.png");
//把一张图片分成两份
int w = im.size().width;
int h = im.size().height;
Mat im1, im2;
Rect rect1(0, 0, w / 2, h);
Rect rect2(w / 2, 0, w / 2, h);
im1 = im(rect1);
im2 = im(rect2);
//图片连接
Mat im_v, im_h;
vconcat(im1, im2, im_v);
hconcat(im1, im2, im_h);
imshow("上下连接", im_v);
imshow("左右连接", im_h);
waitKey(0);
destroyAllWindows();
return 0;
}
在opencv4中提供resize()函数用于修改成指定尺寸。
调用格式resize(row_image,out_image,out_size,w_ratio,h_ratio,method)
row_image:输入的图像
out_image:输出的图像
out_size:改变后的图像的大小
w_ratio:w的比例因子,如果将水平轴变成原来的两倍,则赋值2
h_ratio:h的比例因子,同上
method:改变图像尺寸用的方法,比如缩小图像,就相当于一个下采样,要怎么下采样,比如扩大图像,就需要填充像素,该用什么方法填充像素。一般缩小用INTER_AREA,放大用双三次插值INTER_CUBIC
# include
#include
using namespace std;
using namespace cv;
int main() {
//读取图像
Mat im = imread("E:\\opencv_study\\opencv_study.png");
//获取图像长宽
int w = im.size().width;
int h = im.size().height;
Mat im_up, im_dn;
//放大、缩小图像(改变图像尺寸)
resize(im, im_up, Size(w * 2, h * 2), 0, 0, INTER_CUBIC);
resize(im, im_dn, Size(w / 2, h / 2), 0, 0, INTER_AREA);
imshow("放大图像", im_up);
imshow("缩小图像", im_dn);
waitKey(0);
destroyAllWindows();
return 0;
}
opencv4中使用flip()函数实现图像翻转。
调用格式:flip(row_image,out_image,method)
row_image:输入图像
out_image:输出图像
mehod:翻转方式,大于0表示绕y轴翻转,等于0表示按x轴翻转,小于0表示按x轴跟y轴翻转.
# include
#include
using namespace std;
using namespace cv;
int main() {
//读取图像
Mat im = imread("E:\\opencv_study\\opencv_study.png");
Mat im_x, im_y, im_xy;
//翻转图像
flip(im, im_x, 0);
flip(im, im_y, 1);
flip(im, im_xy, -1);
imshow("原图像", im);
imshow("绕x轴旋转", im_x);
imshow("绕y轴旋转", im_y);
imshow("绕x,y轴旋转", im_xy);
waitKey(0);
destroyAllWindows();
return 0;
}
opencv4使用getRotationMatrix2D()函数计算旋转矩阵,getAffineTransform()通过三个对应点求变换矩阵,warpAffine()函数实现图像的仿射变换,仿射变换就是线性变换+平移,可以保证图像的平行线不变,面积的比值不变。。想仔细了解建议自行百度。
调用格式:
rotation=getRotationMatrix2D(center,angle,scale)
rotation=getAffineTransform(row_point,new_point)
warpAffine(row_image,out_image,rotation,size,flags,border_mode,border_value)
其中:
rotation:旋转矩阵,getRotationMatrix2D函数的返回值,是一个2×3的矩阵。
center:图像旋转的中心位置
angle:图像旋转角度,正值为逆时针旋转,单位为度。
scale:两个轴的比例因子,可以实现缩放,不缩放为1.
row_point:源图像中三个点的坐标
new_point:目标图像中三个点的对应坐标
row_image:原图像
out_image:输出图像
size:输出图像的大小
flags:插值方法标志
border_mode:像素边界外推方法标志
border_value:填充值,默认为0
# include
#include
using namespace std;
using namespace cv;
int main() {
//读取图像
Mat im = imread("E:\\opencv_study\\opencv_study.png");
int w = im.size().width;
int h = im.size().height;
Point2f center(w / 2, h / 2);
double angle = 60;
Mat rotation = getRotationMatrix2D(center, angle, 1);
Mat im_out1,im_out2;
Point2f row_point[3], new_point[3];
row_point[0] = Point2f(0, 0);
row_point[1] = Point2f(0, h);
row_point[2] = Point2f(w, 0);
new_point[0] = Point2f(0.11*w, 0.2*h);
new_point[1] = Point2f(0.15*w, 0.6*h);
new_point[2] = Point2f(0.7*w, 0.3*h);
Mat rotation2 = getAffineTransform(row_point, new_point);
warpAffine(im, im_out1, rotation, Size(w, h));
warpAffine(im, im_out2, rotation2, Size(w, h));
imshow("原图像", im);
imshow("仿射变换", im_out1);
imshow("仿射变换2", im_out2);
waitKey(0);
destroyAllWindows();
return 0;
}
透视变换就是将物体重新投影到新的成像平面,比如摄像机成像,就死透视变换,在opencv4中,使用getPerspectiveTransform函数和warpPerspective函数进行图像的透视变换。
调用格式:
ratation=getPerspectiveTransform(row_point,new_point,method)
warpPerspective(row_image,out_image,rotation,size,flags,border_mode,border_value)
其中:
row_point:原图像的四个像素坐标
new_point:目标图像的四个像素坐标
method:计算透视矩阵的方法
ratation:变换矩阵
row_image:输入图像
out_image:输出图像
size:输出图像大小
flags:插值方法标志
border_mode:像素边界外推方法标志
border_value:填充值,默认为0
# include
#include
using namespace std;
using namespace cv;
int main() {
//图像读取
Mat im = imread("E:\\opencv_study\\opencv_study.png");
int w = im.size().width;
int h = im.size().height;
Point2f row_image[4], new_image[4];
row_image[0] = Point2f(0, 0);
row_image[1] = Point2f(0, h);
row_image[2] = Point2f(w, 0);
row_image[3] = Point2f(w, h);
new_image[0] = Point2f(0.2*w, 0.2*h);
new_image[1] = Point2f(0.2*w, 0.6*h);
new_image[2] = Point2f(0.7*w, 0.3*h);
new_image[3] = Point2f(0.8*w, 0.8*h);
Mat rotation = getPerspectiveTransform(row_image, new_image);
Mat im_out;
warpPerspective(im, im_out, rotation, Size(w, h));
imshow("原图像", im);
imshow("透视变换", im_out);
waitKey(0);
destroyAllWindows();
return 0;
}
# include
#include
using namespace std;
using namespace cv;
int main() {
Mat im = Mat::zeros(Size(512, 512), CV_8UC3);
circle(im, Point(50, 50),25, Scalar(255, 255 ,0), -1);//绘制实心圆
circle(im, Point(50, 20), 25, Scalar(255, 255, 0), 3);//绘制空心圆
line(im, Point(100, 100), Point(200, 100), Scalar(255, 255, 255), 2, LINE_4);//绘制直线
ellipse(im, Point(300, 250), Size(100, 70), 0, 0, 360, Scalar(0, 0, 255), -1);//绘制椭圆
rectangle(im, Rect(200, 150, 100, 60), Scalar(0, 255, 255), -1);//绘制矩形
Point p[5];
p[0] = Point(350, 80);
p[1] = Point(460, 90);
p[2] = Point(500, 170);
p[3] = Point(420, 190);
p[4] = Point(340, 140);
const Point *pts[1] = { p };
int npts[] = { 5 };
fillPoly(im,pts,npts,1, Scalar(0, 255, 255), 8);//绘制多边形
putText(im, "TEST", Point(100, 100), 2, 1, Scalar(255, 0, 255));//绘制文字
imshow("图像显示", im);
waitKey(0);
destroyAllWindows();
return 0;
}
交互操作主要有两个,一个是图像窗口滑动,一个是鼠标响应,在opencv4中,使用createTrackbar函数实现图像窗口滑动,使用setMouseCallback函数实现鼠标响应。
调用格式:
createTrackbar(trackname,winname,value,max_value,callback,user_data)
setMouseCallback(winname,mouse_callback,mouse_data)
其中:
trackname:滑动条名称
winname:窗口名称
value:指向整数变量的指针,该指针指向的值反映滑块的位置,创建后,滑块位置由此变量定义。
max_value:滑动的最大值
callback:回调函数
user_data:传递给回调函数的可选参数
mouse_callback:鼠标响应的回调函数
mouse_data:传递给鼠标响应回调函数的可选参数
# include
#include
using namespace std;
using namespace cv;
int value;
Mat im, im2;
Point prepoint;
static void callback(int ,void*) {
float a = float(value) / 100;
im2 = im * a;
imshow("图像显示", im2);
}
static void mouse(int event, int x, int y, int flags, void *) {
if (event == EVENT_MOUSEMOVE && (flags&EVENT_FLAG_LBUTTON)) {
im.at<Vec3b>(y, x) = Vec3b(0, 0, 255);
im.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255);
im.at<Vec3b>(y, x+1) = Vec3b(0, 0, 255);
im.at<Vec3b>(y-1, x) = Vec3b(0, 0, 255);
im.at<Vec3b>(y, x-1) = Vec3b(0, 0, 255);
imshow("图像显示", im);
}
}
int main() {
im = imread("E:\\opencv_study\\opencv_study.png");
value = 100;
imshow("图像显示", im);
createTrackbar("亮度百分比", "图像显示", &value, 600, callback, 0);
setMouseCallback("图像显示", mouse, 0);
waitKey(0);
destroyAllWindows();
return 0;
}