第二节 图像像素操作
- 1.访问像素值
- 2.用指针扫描图像
- 3.扫描图像并访问相邻像素
- 4.实现简单的图像运算
- 5.图像重映射
1.访问像素值
以椒盐噪声为例展示像素值访问的几种方法
void salt(cv::Mat image, int n) {
// C++11 random number generator
std::default_random_engine generator;
std::uniform_int_distribution<int> randomRow(0, image.rows - 1);
std::uniform_int_distribution<int> randomCol(0, image.cols - 1);
// use image with a Mat_ template 使用模板操作做转换后可直接访问
cv::Mat_<uchar> img(image);
int i,j;
for (int k=0; k<n; k++) {
// random image coordinate
i= randomCol(generator);
j= randomRow(generator);
// 自适应通道数
if (image.type() == CV_8UC1) { // gray-level image
// single-channel 8-bit image
image.at<uchar>(j,i)= 255;
} else if (image.type() == CV_8UC3) { // color image
// 3-channel image
image.at<cv::Vec3b>(j,i)[0]= 255;
image.at<cv::Vec3b>(j,i)[1]= 255;
image.at<cv::Vec3b>(j,i)[2]= 255;
// 直接使用位置访问 下列为单通道赋值
img(j,i)= 255;
// or simply:
// image.at<cv::Vec3b>(j, i) = cv::Vec3b(255, 255, 255);
}
}
}
2.用指针扫描图像
以处理图像像素为例展示指针扫描图像,使用指针并结合位运算的速度远高于逐个计算,double duration = (cv::getTickCount() - start) / cv::getTickFrequency()使用该语句实现算法是耗时分析:
void colorReduceIO(const cv::Mat &image, // input image
cv::Mat &result, // output image
int div = 64) {
int nl = image.rows; // number of lines
int nc = image.cols; // number of columns
int nchannels = image.channels(); // number of channels
// allocate output image if necessary
result.create(image.rows, image.cols, image.type());
//1.指针扫描处理
for (int j = 0; j<nl; j++) {
// get the addresses of input and output row j
// 每一行的开头地址
const uchar* data_in = image.ptr<uchar>(j);
uchar* data_out = result.ptr<uchar>(j);
// 在opencv中按照BGR-BGR逐个排列
for (int i = 0; i<nc*nchannels; i++) {
// process each pixel ---------------------
data_out[i] = data_in[i] / div*div + div / 2;
// end of pixel processing ----------------
} // end of line
}
//2.图像实现运算符重载
int n= static_cast<int>(log(static_cast<double>(div))/log(2.0) + 0.5);
// mask used to round the pixel value
uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
// perform color reduction
image=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
//3. 整图对象遍历
cv::MatIterator_<cv::Vec3b> it= image.begin<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> itend= image.end<cv::Vec3b>();
const cv::Vec3b offset(div/2,div/2,div/2);
for ( ; it!= itend; ++it) {
// process each pixel ---------------------
*it= *it/div*div+offset;
// end of pixel processing ----------------
}
}
3.扫描图像并访问相邻像素
在图像运算中,时长需要访问周边位置的像素,比如边缘提取、边缘锐化,以边缘锐化为例,展示如何访问相邻像素:
void sharpen2D(const cv::Mat &image, cv::Mat &result) {
// 1. 直接使用filter2D进行卷积计算
cv::Mat kernel(3,3,CV_32F,cv::Scalar(0));
// assigns kernel values
kernel.at<float>(1,1)= 5.0;
kernel.at<float>(0,1)= -1.0;
kernel.at<float>(2,1)= -1.0;
kernel.at<float>(1,0)= -1.0;
kernel.at<float>(1,2)= -1.0;
//filter the image
cv::filter2D(image,result,image.depth(),kernel);
//2.通过指针实现高效运算
result.create(image.size(), image.type()); // allocate if necessary
int nchannels= image.channels();
for (int j= 1; j<image.rows-1; j++) { // for all rows (except first and last)
const uchar* previous= image.ptr<const uchar>(j-1); // previous row
const uchar* current= image.ptr<const uchar>(j); // current row
const uchar* next= image.ptr<const uchar>(j+1); // next row
uchar* output= result.ptr<uchar>(j); // output row
for (int i=nchannels; i<(image.cols-1)*nchannels; i++) {
// apply sharpening operator
*output++= cv::saturate_cast<uchar>(5*current[i]-current[i-nchannels]-current[i+nchannels]-previous[i]-next[i]);
}
}
// Set the unprocess pixels to 0 屏蔽边缘像素
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows-1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols-1).setTo(cv::Scalar(0));
}
4.实现简单的图像运算
Mat进行实现图像的一些简单操作:
cv::addWeighted(image1,0.7,image2,0.9,0.,result);
// using overloaded operator
result= 0.7*image1+0.9*image2;
//两种操作等效
5.图像重映射
在日常处理中,诸如投影变换,重采样,等等变换中,需要根据对应像素坐标位置计算目标位置,可通过重映射来快速实现:
void wave(const cv::Mat &image, cv::Mat &result) {
// the map functions
cv::Mat srcX(image.rows,image.cols,CV_32F); // x-map
cv::Mat srcY(image.rows,image.cols,CV_32F); // y-map
// creating the mapping
for (int i=0; i<image.rows; i++) {
for (int j=0; j<image.cols; j++) {
//使用指针会更加高效 这里没有
srcX.at<float>(i,j)= j;
srcY.at<float>(i,j)= i+3*sin(j/6.0);
// horizontal flipping
// srcX.at<float>(i,j)= image.cols-j-1;
// srcY.at<float>(i,j)= i;
}
}
// applying the mapping 重映射函数
cv::remap(image, // source image
result, // destination image
srcX, // x map
srcY, // y map
cv::INTER_LINEAR); // interpolation method
}
该节相关代码已上传到个人文档,按需下载
链接: 点击下载