一、图像中对像素的操作
#include "stdafx.h"
#include <stdio.h>
using namespace std;
using namespace cv;
void salt(Mat &image,int n)
{
for(int k = 0;k < n;k++)
{
int i = rand() % image.cols;
int j = rand() % image.rows;
if(image.channels() == 1)
image.at<uchar>(j,i) = 255;
else if(image.channels() == 3)
{
image.at<Vec3b>(j,i)[0] = 255;
image.at<Vec3b>(j,i)[1] = 255;
image.at<Vec3b>(j,i)[2] = 255;
}
}
}
void main()
{
Mat image = imread("E:\\1.jpg");
namedWindow("img");
imshow("img",image);
salt(image,3000);
namedWindow("image");
imshow("image",image);
waitKey(0);
}
配置:opencv2.4.1+vs2010
代码说明:在像素级别上对图像进行处理,显示了掓盐噪声对图像的影响。椒盐噪声是由图像传感器,传输信道,解码处理等产生的黑白相间的亮暗点噪声。椒盐噪声往往由 图像切割引起。去除脉冲干扰及椒盐噪声最常用的算法是中值滤波
函数说明:cv::Mat中的at(int y,int x)
但是在代码中我们可以看到他的形式是:image.at<Vec3b>(j,i)[0] = 255;
1. 这是为模板类型的函数,因为一个函数的返回类型只有在运行时才会知道。
2.这个函数返回的是一个向量即Vector,故有下标的操作。
3.<Vec3b>代表的意思是:a vector of three 8-bit value。也可以是Vec2b,Vec4b
4.调用模板类型的at()函数有时是效率不高的,因为它的返回类型每次都要由传递的一个模板参数来确定。所以当矩阵的类型已知时,如一个 uchar Matrix,可以利用Mat_类
则 cv::Mat_<uchar> im2= image; // im2 refers to image
im2(50,100)= 0; // access to row 50 and column 100
5、该函数不用于扫描图像,因为效率低,一般用于随机访问像素
处理效果图:
二、用指针扫描图像
代码1:
#include "stdafx.h"
using namespace std;
using namespace cv;
void colorReduce(Mat &img,int div = 128)
{
int nl = img.rows;
int nc = img.cols * img.channels();
for(int j = 0;j < nl;j++)
{
uchar *data = img.ptr<uchar>(j);
for(int i = 0;i < nc;i++)
{
data[i] = data[i]/div*div + div/2;
}
}
}
void main()
{
Mat image = imread("E:\\1.jpg");
namedWindow("img");
imshow("img",image);
Mat imageClone = image.clone();
colorReduce(imageClone);
namedWindow("imageClone");
imshow("imageClone",imageClone);
waitKey(0);
}
代码2:
#include "stdafx.h"
using namespace std;
using namespace cv;
void colorReduce(const Mat &img,Mat &result,int div = 128)
{
int nl = img.rows;
int nc = img.cols * img.channels();
for(int j = 0;j < nl;j++)
{
const uchar *data_in = img.ptr<uchar>(j);
uchar *data_out = result.ptr<uchar>(j);
for(int i = 0;i < nc;i++)
{
data_out[i] = data_in[i]/div*div + div/2;
}
}
}
void main()
{
Mat image = imread("E:\\1.jpg");
namedWindow("img");
imshow("img",image);
Mat result;
result.create(image.rows,image.cols,image.type());
colorReduce(image,result);
namedWindow("imageClone");
imshow("imageClone",result);
waitKey(0);
}
代码3:当图像的数据存储是连续的,即每个像素值的存储之间没有进行过填充,我们可以用一种更高效的访问方式
#include "stdafx.h"
using namespace std;
using namespace cv;
void colorReduce( Mat &img,int div = 128)
{
if(img.isContinuous())
{
img.reshape(1,img.cols*img.rows);
}
int nl = img.rows;
int nc = img.cols * img.channels();
for(int j = 0;j < nl;j++)
{
uchar *data = img.ptr<uchar>(j);
for(int i = 0;i < nc;i++)
{
data[i] = data[i]/div*div + div/2;
}
}
}
void main()
{
Mat image = imread("E:\\1.jpg");
namedWindow("img");
imshow("img",image);
Mat imageClone = image.clone();
colorReduce(imageClone);
namedWindow("imageClone");
imshow("imageClone",imageClone);
waitKey(0);
}
代码说明:以上三个程序完成的都是利用指针扫描图片,并实现图像颜色空间的压缩。
压缩方法:1、data[i]= data[i]/div*div + div/2; 2、data[i]= data[i] – data[i]%div + div/2; 对于第一种当div=pow(2,n)时可以用移位操作:uchar mask= 0xFF<<n;data[i]= (data[i]&mask) + div/2;
函数说明:
代码1:ptr(int j) //它是一个模板函数,返回图像矩阵的第j行的首地址
image.clone() //用于克隆一张图片,但是会产生额外的开销
代码2:
result.create(image.rows,image.cols,image.type());//创建一幅与image同大小及同类型的图像,实际分配内存空间;
//代码2的作用是,不管使用者是否提供两张图片,函数colorReduce(const Mat &img,Mat &result,int div = 128)都能正常工作,且源图像保证不会被改变
代码3:isContinuous()) //用于判断图像的存储是否连续的
reshape(1,img.cols*img.rows) //重新进行图像的维数调整,第一个参数:图像的通道,第二参数:图像的行数。该操作不进行任何的内存拷贝或分配
原图:
处理效果图:
三、利用迭代器遍历图像的像素
void colorReduce( Mat &img,int div = 128)
{
Mat_<cv::Vec3b>::iterator it = img.begin<cv::Vec3b>();
Mat_<cv::Vec3b>::iterator itend = img.end<cv::Vec3b>();
while(it != itend)
{
(*it)[0] = (*it)[0]/div*div + div/2;
(*it)[1] = (*it)[1]/div*div + div/2;
(*it)[2] = (*it)[2]/div*div + div/2;
++it;
}
}
说明:两种方法定义迭代器 1、cv::Mat_<cv::Vec3b>::iterator it; 2、cv::MatIterator_<cv::Vec3b> it;
定义静态迭代器:1、cv::Mat_<cv::Vec3b>::const_iterator it; 2、cv::MatConstIterator_<cv::Vec3b> it;
四、执行时间计时及以上情况的访问速度比较
double duration;
duration = static_cast<double>(cv::getTickCount());
colorReduce(image); // the function to be tested
duration = static_cast<double>(cv::getTickCount())-duration;
duration /= cv::getTickFrequency(); // the elapsed time in ms
函数说明:getTickCount() //获得当前的时钟周期数
getTickFrequency() //机器每秒执行的时钟周期数
五、图像颜色空间压缩的最快的一种代码方式
void colorReduce(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols ; // number of columns
// is it a continous image?
if (image.isContinuous()) {
// then no padded pixels
nc= nc*nl;
nl= 1; // it is now a 1D array
}
int n= static_cast<int>(
log(static_cast<double>(div))/log(2.0));
// mask used to round the pixel value
uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
// for all pixels
for (int j=0; j<nl; j++) {
// pointer to first column of line j
uchar* data= image.ptr<uchar>(j);
for (int i=0; i<nc; i++) {
// process each pixel ---------------------
*data++= *data&mask + div/2;
*data++= *data&mask + div/2;
*data++= *data&mask + div/2;
// end of pixel processing ---------------- } // end of line
}
}