OpenCV—访问图像中的像素

简介: OpenCV—访问图像中的像素

言: OpenCV3编程入门已经进入第五章 (Core组件进阶) 学习了,后面也越来越多对图像的处理,这一节主要是对像素的认识。

一、访问图形中的像素

1、图像在内存之中的存储方式

图像矩阵的大小取决于所用的颜色模型,也就是说取决于所用的通道数。如下灰度图像:

对于多通道的图像,矩阵中的列会包含多个子列,子列个数与通道数相等。

2、颜色空间缩减

(1)问题提出:对于单通道像素,有256个不同的值,若三通道图像,颜色的存储值就有一千六百万多种。

解决: 如此之多的颜色数值,但是达到同样效果的只占很小一部分。颜色空间缩减(color space reduction)可以大大降低运算复杂度。它的做法:将现有颜色空间值除以某个输入值,以获得较少的颜色数,比如:0-9可以取0,10-19取10,以此类推。

(2)uchar类型的三通道图像,每个通道取值0~255,也就是256x256不同的值。

  • 0~9范围的像素值为0
  • 10~19范围的像素值是10
  • 20~29范围的像素值是20

这样操作,将颜色取值降低为26x26x26种情况。即:Inew=(Iold/10)*10。因为uchar类型的值除以int值,结果仍是char类型,所以求出来的小数也要向下取整。

(3)为了防止处理图像像素时,每一步计算都会花销时间,于是可以把计算好的结果提前存入表table中,这样不需要计算,直接取结果即可。

// 1、遍历图像矩阵的每一个像素
// 2、对像素应用上述公式
int divideWith=10;
uchar table[256];
for(int i=0;i<256;i++){
    table[i]=divideWith*(i/divideWith);//table[i]存放值为i像素减小颜色空间结果
}
//p[j]=table[p[j]];

总结:对于较大的图像,有效的方法是预先计算所有可能的值,然后需要这些值的时候,利用查找表直接赋值即可。

3、LUT函数:Look up table操作

说明:函数原型为:operationsOnArrays:LUT()的函数来进行,用于批量进行图像元素查找、扫描与操作图像。

//首先建立一个Mat型用于查表
Mat lookUpTable(1,256,CV_8U);
uchar* p=lookUpTable.data;
for(int i=0;i<256;i++){
  p[i]=table[i];
}
//然后调用函数(I输入,J输出)
for(int i=0;i<times;i++){
  LUT(I,lookUpTable,J);
}

这个地方书上讲的有点模糊,不是很懂,就去参考其他大佬

(1)对于单通道图像,其像素灰度值为0-255,LUT函数可以将函数的一个灰度值转换成其他灰度值。如:将一张图片0-100的像素的灰度值变成0,101-200的像素变成100,201-255的像素变成255。

(2)对于多通道图像,其内部每一个通道都做这样的映射,这样每一个通道的处理就和单通道差不多了。

(3)LUT函数为:

void LUT(InputArray src,InpitArray lut,OutputArray dst);
  • src: 输入图像(单通道或者3通道)
  • lut: 表示查找表,下面会用具体示例介绍
  • dst: 输出图像

(4)单通道图像处理

#include<opencv2/highgui.hpp>
using namespace cv;
int main()
{
  uchar lutData[256];
  for (int i = 0; i < 256; i++)
  {
    if (i <= 100)
      lutData[i] = 0;
    if (i > 100 && i <= 200)
      lutData[i] = 100;
    if (i > 200)
      lutData[i] = 255;
  }
  Mat lut(1, 256, CV_8UC1);
  Mat a = imread("12.jpg", CV_LOAD_IMAGE_GRAYSCALE),b;
  namedWindow("原灰度图像", CV_WINDOW_AUTOSIZE);
  namedWindow("转灰度图像", CV_WINDOW_AUTOSIZE);
  imshow("原灰度图像", a);
  LUT(a, lut, b);
  imshow("转灰度图像", b);
  waitKey(0);
}

4、计时函数

说明:计时函数:getTickCount()和getTickFrequency()

  • getTickCount()函数返回CPU自某个事件(如启动电脑)以来走过的时钟周期数
  • getTickFrequency()函数返回CPU一秒钟走的时钟周期数,我们就可以以秒为单位对某运算计时。
double time0=static_cast<double>(getTickCount());//记录起始时间
//进行图像处理操作。。。。
time0=((double)getTickCount()-time0)/getTickFrequency();
printf("输出运行时间%f\n",time0);
5、访问图像像素的三类方法

说明:任何图像处理算法,都是从操作每个像素开始的。

提供三种访问每个像素的方法

  • 指针访问:C操作符[ ];
  • 迭代器iterator;
  • 动态地址计算

这三种方法在访问速度上略有差异,在debug模式下,比较明显。程序的目的是减少图像中颜色的数量,比如原来的图像是256种颜色,变成64只需要将原来的颜色除以4再乘以4.

示例程序:主程序中调用colorReduce函数来完成减少颜色的工作。

#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//全局函数声明
void colorReduce(Mat &inputImage, Mat& outputImage, int div);

int main()
{
  Mat srcImage = imread("12.jpg");
  imshow("原始图像",srcImage);
  
  //将原始图的参数规格来创建效果图
  Mat dstImage;
  dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());//效果图的大小、类型和原图片相同

  //记录起始时间
  double time0 = static_cast<double>(getTickCount());

  //调用颜色空间缩减函数
  colorReduce(srcImage, dstImage, 128);

  //计算运行时间并输出
  time0 = ((double)getTickCount() - time0) / getTickFrequency();
  printf("此方法运行时间为:%f\n", time0);
  imshow("效果图",dstImage);
  waitKey(0);
}
(1)用指针访问像素

说明:用指针访问像素发这种方法利用的是C语言操作符[ ],这种方法比较快。

void colorReduce(Mat &inputImage, Mat& outputImage, int div)
{
  //参数准备
  outputImage = inputImage.clone();//复制实参到临时变量
  int rowNumber = outputImage.rows;//行数
  int colNumber = outputImage.cols*outputImage.channels();//列数x通道数=每一行元素的个数
  //双重循环,遍历所有的像素值
  for (int i = 0; i < rowNumber; i++) {//行循环
    uchar* data = outputImage.ptr<uchar>(i);//获取第i行的首地址
    for (int j = 0; j < colNumber; j++) { //列循环
      //处理每一个像素
      data[j] = data[j] / div * div + div / 2;
    }
  }
}

补充:简化指针运算,Mat类提供了ptr函数可以得到图像任意行的首地址。

uchar* data = outputImage.ptr<uchar>(i);//获取第i行的首地址
(2)用迭代器操作像素

说明:在迭代法中,获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加在迭代指针前。

/*
  迭代器
*/
void colorReduce(Mat &inputImage, Mat& outputImage, int div)
{
  //参数准备
  outputImage = inputImage.clone();//复制实参到临时变量
  //获取迭代器
  Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();//初始位置迭代器
  Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();//终止位置迭代器
  //获取彩色图像像素
  for (; it != itend; it++) {
    //处理每一个像素
    (*it)[0] = (*it)[0] / div * div + div / 2;//蓝
    (*it)[1] = (*it)[1] / div * div + div / 2;//绿
    (*it)[2] = (*it)[2] / div * div + div / 2;//红
  }
}
(3)动态地址计算

说明:使用动态地址运算配合at方法的colorReduce函数的代码,这种方法简洁,便于对像素的直观认识。

/*
  动态地址法
*/
void colorReduce(Mat &inputImage, Mat& outputImage, int div)
{
  //参数准备
  outputImage = inputImage.clone();//复制实参到临时变量
  int rowNumber = outputImage.rows;//行数
  int colNumber = outputImage.cols;//行数

  //获取彩色图像像素
  for (int i = 0; i < rowNumber; i++) {
    for (int j = 0; j < colNumber; j++) {
      //开始处理每一个像素,0/1/2表示通道数
      outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2;//蓝色通道
      outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2;//绿色通道
      outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2;//红色通道
    }
  }
}

补充:

  • Vec3b:由三个unsigned char组成的向量
  • image.at(i,j):取出灰度图像中i行j列的点。
  • image.at(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点,k=[0,1,2],分别代表B,G,R。

实际效果:

相关文章
|
2天前
|
计算机视觉
OpenCV图像运动模糊
OpenCV图像运动模糊
8 0
|
2天前
|
计算机视觉
OpenCV图像阈值
OpenCV图像阈值
5 0
|
2天前
|
计算机视觉
OpenCV图像混合
OpenCV图像混合
7 0
|
2天前
|
计算机视觉 Python
OpenCV为图像扩边(填充)
OpenCV为图像扩边(填充)
9 0
|
2天前
|
计算机视觉 Python
轻松掌握opencv的8种图像变换
轻松掌握opencv的8种图像变换
|
2天前
|
算法 计算机视觉
【OpenCV】- 图像修复
【OpenCV】- 图像修复
|
2天前
|
Serverless 计算机视觉
【OpenCV】-图像的矩
【OpenCV】-图像的矩
|
2天前
|
编解码 物联网 计算机视觉
【OpenCV】—图像金子塔与图片尺寸缩放
【OpenCV】—图像金子塔与图片尺寸缩放
|
2天前
|
前端开发 计算机视觉 C++
【OpenCV】—分离颜色通道、多通道图像混合
【OpenCV】—分离颜色通道、多通道图像混合
|
2天前
|
API 计算机视觉
【OpenCV】—图像对比度、亮度值调整
【OpenCV】—图像对比度、亮度值调整