OpenCV学习笔记之扫描图像

亦凉 2023-07-14 10:44 74阅读 0赞

一、 图像在内存中存储方式

图像矩阵的大小取决于我们所用的颜色模型,确切地说,取决于所用通道数。如果是灰度图像,矩阵就会像这样:

\\newcommand\{\\tabItG\}\[1\] \{ \\textcolor\{black\}\{\#1\} \\cellcolor\[gray\]\{0.8\}\} \\begin\{tabular\} \{ccccc\} ~ & \\multicolumn\{1\}\{c\}\{Column 0\} &   \\multicolumn\{1\}\{c\}\{Column 1\} &   \\multicolumn\{1\}\{c\}\{Column ...\} & \\multicolumn\{1\}\{c\}\{Column m\}\\\\ Row 0 & \\tabItG\{0,0\} & \\tabItG\{0,1\} & \\tabItG\{...\}  & \\tabItG\{0, m\} \\\\ Row 1 & \\tabItG\{1,0\} & \\tabItG\{1,1\} & \\tabItG\{...\}  & \\tabItG\{1, m\} \\\\ Row ... & \\tabItG\{...,0\} & \\tabItG\{...,1\} & \\tabItG\{...\} & \\tabItG\{..., m\} \\\\ Row n & \\tabItG\{n,0\} & \\tabItG\{n,1\} & \\tabItG\{n,...\} & \\tabItG\{n, m\} \\\\ \\end\{tabular\}

而对多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等。例如,RGB颜色模型的矩阵:

\\newcommand\{\\tabIt\}\[1\] \{ \\textcolor\{yellow\}\{\#1\} \\cellcolor\{blue\} &  \\textcolor\{black\}\{\#1\} \\cellcolor\{green\} & \\textcolor\{black\}\{\#1\} \\cellcolor\{red\}\} \\begin\{tabular\} \{ccccccccccccc\} ~ & \\multicolumn\{3\}\{c\}\{Column 0\} &   \\multicolumn\{3\}\{c\}\{Column 1\} &   \\multicolumn\{3\}\{c\}\{Column ...\} & \\multicolumn\{3\}\{c\}\{Column m\}\\\\ Row 0 & \\tabIt\{0,0\} & \\tabIt\{0,1\} & \\tabIt\{...\}  & \\tabIt\{0, m\} \\\\ Row 1 & \\tabIt\{1,0\} & \\tabIt\{1,1\} & \\tabIt\{...\}  & \\tabIt\{1, m\} \\\\ Row ... & \\tabIt\{...,0\} & \\tabIt\{...,1\} & \\tabIt\{...\} & \\tabIt\{..., m\} \\\\ Row n & \\tabIt\{n,0\} & \\tabIt\{n,1\} & \\tabIt\{n,...\} & \\tabIt\{n, m\} \\\\ \\end\{tabular\}

注意到,子列的通道顺序是反过来的:BGR而不是RGB。很多情况下,因为内存足够大,可实现连续存储,因此,图像中的各行就能一行一行地连接起来,形成一个长行。连续存储有助于提升图像扫描速度,我们可以使用 isContinuous() 来去判断矩阵是否是连续存储的. 相关示例会在接下来的内容中提供。

二、扫描图像几种方式

2.1 指针访问

  1. /*!
  2. * \brief scanImagePtr
  3. * \param image
  4. * \return
  5. */
  6. Mat& scanImagePtr(Mat &image){
  7. double start = static_cast<double>(getTickCount());
  8. //! 行
  9. int rowNumber = image.rows;
  10. //! 每一行元素个数 = 列数 x 通道数
  11. int colNumber = image.cols * image.channels();
  12. for (int i = 0; i < rowNumber; i++)
  13. {
  14. //! 获取第i行的首地址
  15. uchar* data = image.ptr<uchar>(i);
  16. for (int j = 0; j < colNumber; j++)
  17. {
  18. data[j] /= 2;
  19. }
  20. }
  21. double end = static_cast<double>(getTickCount());
  22. double time = (end - start) / getTickFrequency();
  23. cout << "type1:" << time << "s" << endl;
  24. return image;
  25. }

2.2 迭代器iterator

  1. /*!
  2. * \brief scanImageiterator
  3. * \param image
  4. * \return
  5. */
  6. Mat& scanImageiterator(Mat &image){
  7. double start = static_cast<double>(getTickCount());
  8. //! 初始位置的迭代器
  9. Mat_<Vec3b>::iterator it = image.begin<Vec3b>();
  10. //! 终止位置的迭代器
  11. Mat_<Vec3b>::iterator itend = image.end<Vec3b>();
  12. for (; it != itend; it++)
  13. {
  14. //! 处理BGR三个通道
  15. (*it)[0] = 255;//B
  16. //(*it)[1] = 255;//G
  17. //(*it)[2] = 0;//R
  18. }
  19. double end = static_cast<double>(getTickCount());
  20. double time = (end - start) / getTickFrequency();//计算时间
  21. cout << "type2:" << time << "s" << endl;
  22. return image;
  23. }

2.3 动态地址计算

  1. /*!
  2. * \brief scanImageAt
  3. * \param image
  4. * \return
  5. */
  6. Mat& scanImageAt(Mat &image){
  7. double start = static_cast<double>(getTickCount());
  8. int rowNumber = image.rows;
  9. int colNumber = image.cols;
  10. for (int i = 0; i < rowNumber; i++)
  11. for (int j = 0; j < colNumber; j++)
  12. {
  13. //! 处理BGR三个通道
  14. //image.at<Vec3b>(i, j)[0] = 0;//B
  15. image.at<Vec3b>(i, j)[1] = 255;//G
  16. //image.at<Vec3b>(i, j)[2] = 0;//R
  17. }
  18. double end = static_cast<double>(getTickCount());
  19. double time = (end - start) / getTickFrequency();//计算时间
  20. cout << "type3:" << time << "s" << endl;
  21. return image;
  22. }

2.4 三种方法效能对比

  1. type1:0.000189559s
  2. type2:0.000832323s
  3. type3:0.000424337s

经典的C风格运算符[](指针)访问要更胜一筹。

代码详见《OpenCV扫描图像三种方式练习》

三、OpenCv坐标系

OpenCV中坐标体系中的零点坐标定义为图片的左上角,X轴为图像矩形的上面那条水平线,从左往右;Y轴为图像矩形左边的那条垂直线,从上往下。在Point(x,y)和Rect(x,y)中,第一个参数x代表的是元素所在图像的列数,第二个参数y代表的是元素所在图像的行数,而在at(x,y)中是相反的。

SouthEast

参考资料:

  • OpenCV坐标系与操作像素的四种方法
  • OpenCV如何扫描图像、利用查找表和计时
  • Opencv中三种操作像素的方法

发表评论

表情:
评论列表 (有 0 条评论,74人围观)

还没有评论,来说两句吧...

相关阅读

    相关 OpenCV学习笔记扫描图像

    一、 图像在内存中存储方式 图像矩阵的大小取决于我们所用的颜色模型,确切地说,取决于所用通道数。如果是灰度图像,矩阵就会像这样: ![\\newcommand\{\\t