图像处理中的概念
图像高频部分代表了图像的细节、纹理信息;低频代表了图像的轮廓信息。
低通-》模糊
高通-》锐化
腐蚀和膨胀是针对白色部分(高亮部分)而言的。膨胀就是对图像高亮部分进行“领域扩张”,效果图拥有比原图更大的高亮区域;腐蚀是原图中的高亮区域被蚕食,效果图拥有比原图更小的高亮区域。
开运算:先腐蚀再膨胀,用来消除小物体
闭运算:先膨胀再腐蚀,用于排除小型黑洞
形态学梯度:就是膨胀图与俯视图之差,用于保留物体的边缘轮廓。
顶帽:原图像与开运算图之差,用于分离比邻近点亮一些的斑块。
黑帽:闭运算与原图像之差,用于分离比邻近点暗一些的斑块。
rows:行
cols:列
常用的数据结构
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
//常见数据结构使用方法总结
int main()
{
//Mat的用法
Mat m1(2, 2, CV_8UC3, Scalar(0, 0, 255)); //其中的宏的解释:CV_[位数][带符号与否][类型前缀]C[通道数]
cout << m1 << endl;
//或者,利用IplImage指针来初始化,将IplImage*转化为Mat
IplImage* image = cvLoadImage("lena.jpg");
Mat mat = cvarrToMat(image);
//Mat转IplImage:
IplImage img = IplImage(mat);
//或者
Mat m2;
m2.create(4, 5, CV_8UC(2));
//点的表示:Point
Point p;
p.x = 1; //x坐标
p.y = 1; //y坐标
//或者
Point p2(1, 1);
//颜色的表示:Scalar(b,g,r);注意不是rgb,注意对应关系
Scalar(1, 1, 1);
//尺寸的表示:Size
Size(5, 5);// 宽度和高度都是5
//矩形的表示:Rect,成员变量有x,y,width,height
Rect r1(0, 0, 100, 60);
Rect r2(10, 10, 100, 60);
Rect r3 = r1 | r2; //两个矩形求交集
Rect r4 = r1 & r2; //两个矩形求并集
waitKey(0);
访问图片中像素的方式
Mat类有若干成员函数可以获取图像的属性。cols表示列,rows表示行,channels()返回图像的通道数,灰度图通道数1,彩色图通道数3。每行的像素值由一下语句得到:
int colNumber = outputImage.cols*outputImage*channels();//列数*通道数=每一行元素个数 (有点三维立体的感觉)
为了简化指针运算,Mat类提供了ptr函数可以得到图像任意行的首地址。ptr是一个模版函数,它返回第i行的首地址:
uchar* data = img.ptr<uchar>(i); //获取第i行地址
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
//使用指针的方式
int main()
{
Mat img = imread("lol1.jpg");
for (int i = 0; i < img.rows; i++)
{
uchar* data = img.ptr<uchar>(i); //获取第i行地址
for (int j = 0; j < img.cols; j++)
{
printf("%d\n",data[j]);
}
}
waitKey(0);
}
感兴趣区域:ROI
在图像处理领域,常常需要设定感兴趣区域(ROI,region of interest)来专注或者简化工作过程,即在图像中选择一个图像区域,这个区域是图像分析关注的重点。而且使用ROI指定想读入的目标,可以减少处理时间,增加精度,带来便利。
定义ROI区域两种方法:
1.使用表示矩形区域的Rect
2.Range指定感兴趣的行或列的范围
基础图像操作
创建窗口:namedWindow()
void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE);
因为有时候需要用到窗口的名字,尽管这个时候还没有载入图片,比如我们要在一个窗口上加入一个工具条,我们必须首先知道窗口的名字,这样才知道在哪里加上这个toolbar。
namedWindow还有一个很重要的功能,如果使用默认参数,窗口是无法自由调整的,如果想实现用户自由拉伸窗口,可以这么做:
namedWindow("srcImage", WINDOW_NORMAL);// 注意这个宏,使用WINDOW_NORMAL可以允许用户自由伸缩窗口大小
imshow("srcImage", srcImage);
输出图像到文件:imwrite()
bool imwrite( const String& filename, InputArray img,const std::vector¶ms = std::vector());
创建trackbar以及使用
下面的例子利用trakbar打开多个图片。
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
#define PIC_MAX_NUM 5
int pic_num = 0;
void on_track(int,void*)
{
char file[10];
sprintf(file, "%d.jpg", pic_num);
Mat img = imread(file);
if (!img.data)
{
cout << "读取图片失败" << endl;
return;
}
imshow("展示多幅图片", img);
}
int main()
{
namedWindow("展示多幅图片");
createTrackbar("图片编号", "展示多幅图片", &pic_num, PIC_MAX_NUM, on_track);
on_track(pic_num, NULL);
waitKey(0);
}
转为灰度图
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("lol1.jpg");
Mat dstImg;
cvtColor(img, dstImg,COLOR_BGR2GRAY);//从宏名字就可以知道,是彩色图转换到灰度图
imshow("灰度图", dstImg);
waitKey(0);
}
图像二值化操作
两种方法,全局固定阈值二值化和局部自适应阈值二值化
1.全局固定阈值很容易理解,就是对整幅图像都是用一个统一的阈值来进行二值化;
2.局部自适应阈值则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat image = imread("lol1.jpg", CV_LOAD_IMAGE_GRAYSCALE); //注意了,必须是载入灰度图
if (image.empty())
{
cout << "read image failure" << endl;
return -1;
}
// 全局二值化
int th = 100;
Mat global;
threshold(image, global, th, 255, CV_THRESH_BINARY_INV);
// 局部二值化
int blockSize = 25;
int constValue = 10;
Mat local;
adaptiveThreshold(image, local, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);
imshow("全局二值化", global);
imshow("局部二值化", local);
waitKey(0);
return 0;
}
Canny边缘检测
思路:将原始图像转化为灰度图,用blur函数进行图像模糊以降噪,然后用Canny函数进行边缘检测。
需要注意的是,这个函数阈值1和阈值2两者中较小的值用于边缘连接,而较大的值用来控制强边缘的初始端,推荐的高低阈值比在2:1和3:1之间。
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat SrcPic = imread("1.jpg");
imshow("Src Pic", SrcPic);
Mat DstPic, edge, grayImage;
//创建与src同类型和同大小的矩阵
DstPic.create(SrcPic.size(), SrcPic.type());
//将原始图转化为灰度图
cvtColor(SrcPic, grayImage, COLOR_BGR2GRAY);
//先使用3*3内核来降噪 edge为均值滤波后图像,需与原图像大小一样
blur(grayImage, edge, Size(3, 3));
//运行canny算子,最后一个3 是默认算子孔径
Canny(edge, edge, 3, 9, 3);
imshow("边缘提取效果", edge);
waitKey();
return 0;
}
直方图均衡化
直方图 可以用来反映灰度情况(0-255分布情况),每个等级的频数分布
直方图均值化拉伸图像的灰度值范围,使得两头变多(0和255),所以提高了对比度,显然均衡化后的图片对比度变高了,变得更加明亮。
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
//直方图均衡化
int main()
{
Mat img = imread("3.jpg");
imshow("原始图", img);
Mat dst;
cvtColor(img, img, CV_RGB2GRAY);
imshow("灰度图", img);
equalizeHist(img, dst);
imshow("直方图均衡化", dst);
waitKey(0);
}