基于OpenCV的图像编程基础
主要内容
1、配置基于VS2017和OpenCV3.1.0的编程环境。
2、 读入、显示及存储图像。
3、 编写三种遍历扫描整幅图像的方法,对灰度图像增强亮度,对彩色图像加入300个椒盐噪声,以此掌握如何操作像素。
4、对彩色图像增加亮度。
一、配置编程环境
1. 下载安装包
在官网
https://sourceforge.net/projects/opencvlibrary/
下载OpenCV3.1.0的安装包。
2. 解压文件
打开压缩包,按照提示安装在路径“D:\opencv”下。
3. 配置环境变量
在设置的搜索框中搜索“环境变量”,并选择“编辑系统环境变量”。
编辑Path,弹出对话框后,点击新建。
输入OpenCV安装解压后的路径后点击完成。
4. 配置编译器
依此点击【文件】→【新建】→【项目】→【控制台应用程序】,结果如下图所示:
右击工程文件夹,选择属性,结果如图所示:
在【VC++目录】→【包含目录】→【编辑】下添加三条路径
D:\opencv\build
D:\opencv\build\include\opencv
D:\opencv\build\include\opencv2
在【VC++目录】→【库目录】→【编辑】下添加一条路径
D:\opencv\build\x64\vc14\lib
在【连接器】→【附加依赖项】→【编辑】下添加两个文件
opencv_world310d.lib
opencv_world310.lib
点击完成则配置完成
二、OpenCV读入、显示、存储图像的相关函数
首先需要定义一个用于储存图像的变量,在OpenCV中,通过定义从cv::Mat类的对象来实现。
cv::Mat image;
1. 读入图片
调用imread()函数可以读取图像文件,该函数读入文件图像并对其经行编码,分配内存,例如:
image = cv::imread(“E://test1//pepper.bmp”);
2. 显示图片
需要调用cv::namedWindow()创建一个显示窗口,例如:
cv::namedWindow(“Original Image”);
调用cv::imshow()来显示图像,例如:
cv::imshow(“Original Image”,image);
3. 储存图片
调用cv::imwrite()函数存储处理后的图像,例如:
cv::imwrite(“E://test1//output.bmp”,result);
三、遍历图像数据的方法
1. at方法
cv::Mat提供了一个at(int y, int x)函数模板来操作指定位置的矩阵元素,在使用时需要指定函数返回的数据类型,例如:
image.at<unchar>(j,i)=255;
//采用嵌套循环的方式便可以实现遍历,代码示例如下:
void light(Mat& inputImage, Mat& outputImage, int add)
{
int i, j;
outputImage = inputImage.clone();
int rows = outputImage.rows;
int cols = outputImage.cols;
for ( i = 0; i < rows; i++) {
for ( j = 0; j < cols; j++) {
outputImage.at<Vec3b>(i,j)[0] = saturate_cast<uchar>(outputImage.at<Vec3b>(i, j)[0] +add);
outputImage.at<Vec3b>(i,j)[1] = saturate_cast<uchar>(outputImage.at<Vec3b>(i, j)[1] +add);
outputImage.at<Vec3b>(i,j)[2] = saturate_cast<uchar>(outputImage.at<Vec3b>(i, j)[2] +add);
}
}
}
2. 使用迭代器
类似于STL库的使用方法,OpenCV可以采用迭代器实现对像素的操作,代码示例如下:
void light (Mat& inputImage, Mat& outputImage, int add)
{
outputImage = inputImage.clone();
Mat_<Vec3b>::iterator it = inputImage.begin<Vec3b>();
Mat_<Vec3b>::iterator itend = inputImage.end<Vec3b>();
Mat_<Vec3b>cimage = outputImage;
Mat_<Vec3b>::iterator itout = cimage.begin();
Mat_<Vec3b>::iterator itoutend = cimage.end();
for (; it != itend; it++,itout++) {
(*itout)[0] = saturate_cast<uchar>((*it)[0] + add);
(*itout)[1] = saturate_cast<uchar>((*it)[1] + add);
(*itout)[2] = saturate_cast<uchar>((*it)[2] + add);
}
}
3. 使用指针
cv::Mat提供一个print(int i)函数模板来获取指定行数据的首地址。利用这一地址可以实现对像素的操作。代码示例如下:
void light(Mat& inputImage, Mat& outputImage, int add)
{
int i, j;
outputImage = inputImage.clone();
int rows = outputImage.rows;
int cols = outputImage.cols * outputImage.channels();
for (i = 0; i < rows; i++) {
uchar * data = inputImage.ptr<uchar>(i);
uchar * dataout = outputImage.ptr<uchar>(i);
for (j = 0; j < cols; j++) {
dataout[j] = saturate_cast<uchar>(dataout[j]+add);
}
}
}
四、增加色彩亮度
为了增加彩色图的亮度,需要先遍历每个像素点。三个通道加上一个相同的数值即可,提高亮度。
五、结果分析
1. 读入、显示及存储图像
这里读入了一张图片,并将它经行轴对称变换,显示并储存。代码如下:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat image, result;
cout << "size:" << image.size().height << "," << image.size().width << endl;
image = imread("E://test1//pepper.bmp");
if (!image.data) {
//检查是否读取图像
cout << "Error! Input image cannot be read...\n";
}
namedWindow("Original Image");
imshow("Original Image", image);
flip(image, result, 1);
namedWindow("Output Image");
imshow("Output Image", result);
imwrite("E://test1//output.bmp", result);
waitKey(0); // Wait for key press
return 0;
}
图像对称变换
处理后的图片储存的相应的文件夹下
2. 遍历图像
这里以使用迭代器的方法为例子,对灰度图像增加亮度,即为像素点的单通道增加一个定值,代码示例如下(这里使用了saturate_cast()函数作为溢出保护,防止图片失真):
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
void colourReduce(Mat& inputImage, Mat& outputImage, int add)
{
outputImage = inputImage.clone();
Mat_<Vec3b>::iterator it = inputImage.begin<Vec3b>();
Mat_<Vec3b>::iterator itend = inputImage.end<Vec3b>();
Mat_<Vec3b>cimage = outputImage;
Mat_<Vec3b>::iterator itout = cimage.begin();
Mat_<Vec3b>::iterator itoutend = cimage.end();
for (; it != itend; it++,itout++) {
(*itout)[0] = saturate_cast<uchar>((*it)[0] + add);
(*itout)[1] = saturate_cast<uchar>((*it)[1] + add);
(*itout)[2] = saturate_cast<uchar>((*it)[2] + add);
}
}
int main()
{
Mat in_image, out_image;
in_image = imread("E://test2.2//cameraman.jpg");
if (!in_image.data) {
//检查是否读取图像
cout << "Error! Input image cannot be read...\n";
}
namedWindow("Original Image");
imshow("Original Image", in_image);
colourReduce(in_image, out_image, 100);
namedWindow("Output Image");
imshow("Output Image", out_image);
imwrite("E://test2.2//outputImage2.1.jpg", out_image);
waitKey(0); // Wait for key press
return 0;
}
结果为增加灰度图的亮度
椒盐噪声是指两种噪声:盐噪声和椒噪声。盐噪声一般是白色噪声,椒噪声一般是黑色噪声。前者属于高灰度噪声,或者属于低灰度噪声,一般两种噪声同时出现,呈现黑白杂点。
对彩色图像加入300个椒盐噪声。即在随机的行和列上,将通道的值设为255或0,这里使用at方法进行遍历,示例代码如下:
void addsaltnoise(Mat& inputImage, Mat& outputImage, int n)
{
int m,f;
outputImage = inputImage.clone();
for (m = 0; m < n; m++) {
int i = rand() % outputImage.rows;
int j = rand() % outputImage.cols;
f = rand() % 2;
if (f == 0) {
outputImage.at<Vec3b>(i, j)[0] = 255; //添加盐噪声
outputImage.at<Vec3b>(i, j)[1] = 255;
outputImage.at<Vec3b>(i, j)[2] = 255;
}
else {
outputImage.at<Vec3b>(i, j)[0] = 0; //添加椒噪声
outputImage.at<Vec3b>(i, j)[1] = 0;
outputImage.at<Vec3b>(i, j)[2] = 0;
}
}
结果为插入300个椒盐点后的图片
3. 对彩色图像增加亮度
对彩色图增加亮度,即为三个通道各加上一个定值,这里用at方法进行遍历,示例代码如下:
void light(Mat& inputImage, Mat& outputImage, int add)
{
int i, j;
outputImage = inputImage.clone();
int rows = outputImage.rows;
int cols = outputImage.cols;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
outputImage.at<Vec3b>(i,j)[0]= saturate_cast<uchar>(outputImage.at<Vec3b>(i, j)[0] + add);
outputImage.at<Vec3b>(i,j)[1]=
saturate_cast<uchar>(outputImage.at<Vec3b>(i, j)[1] + add);
outputImage.at<Vec3b>(i,j)[2]= saturate_cast<uchar>(outputImage.at<Vec3b>(i, j)[2] + add);
}
}
}
结果为增加亮度后的彩色图