数字图像处理作业使用OpenCV - 块提取

今天要记录的是树图第二次作业的第二题,Image Patch Extraction。这个概念真的不难懂,但是如果要我实际写的话,还真的不知道要怎么去遍历图像矩阵来提取块。在此要多谢邓大神的热心帮助,告诉了我一个遍历的思路_(:з」∠)_

一开始我是从原图像矩阵来考虑循环的,也就是两层循环的边界分别是原图像的width和height。这样思考的话,我完全不知道应该如何移动这个patch。

之后思考了从得到的patch列表来考虑,也就两侧循环的边界分别是W-w+1和H-h+1(W是原图像的width,H是原图像的height,w和h分别是patch的宽度和高度),这样思考之后我依然卡住,虽然得到的patch列表是方便了,但是怎么样用这几个索引变量来定位到原图上的像素让我苦恼了很久。

请教过邓大神之后,得到一个确切可行的循环,大致如下:

1 for(i : W-w+1)
2    for(j : H-h+1)
3       for(ii : w)
4          for(jj : h)

跟第二次想到的是一样的思路,而因为这是确定正确的循环,所以思考的时候就不会怕自己出错。加上看过了他的java代码后,知道要定位到原图像素,可以直接用img.ptr<uchar>(i+ii)[j+jj]来定位,也就是二维坐标定位,指向的像素点坐标为(i+ii, j+jj)。觉得自己语言说不清楚,还是努力画个图来说明一下。
老是会把图像的存储模式定性为线性存储的习惯要好好改改了_(:з」∠)_

而提取出来的块存在一个三维定长数组中,patches[W-w+1][H-h+1][w*h],从左到右的下标表示在第几行、在第几列的矩阵中的哪个元素,所以准确来说每一个patch是用一个长度为w*h的一维数组存储的,所以访问元素的时候还是要按照线性的方法来,ii*w+jj。前面两个参数就是表示分解出来的矩阵的坐标了,组成这个矩阵的基本元素是一个w*h的数组。

一开始我是用三层vector的push_back来存patch,代码如下

 1     vector<vector<vector<uchar>>> patches;
 2     for (int i = 0; i < rows; i++) {
 3         vector<vector<uchar>> pCols;
 4         for (int j = 0; j < cols; j++) {
 5             vector<uchar> patch;
 6             for (int ii = 0; ii < h; ii++) {
 7                 for (int jj = 0; jj < w; jj++) {
 8                     patch.push_back(img.ptr<uchar>(i+ii)[j+jj]);
 9                 }
10             }
11             pCols.push_back(patch);
12         }
13         patches.push_back(pCols);
14     }

结果发现,运行计算patches竟然要用三分多钟,听说一般一分钟都不用的时间,我跑出了这么长的时间也是吓快了_(:з」∠)_想了一下,应该是因为vector是动态分配的数组,而每一次进入新的循环都创建了新的vector,这部分费时让总时间变得那么长,所以就复习了一下如何用new分配定长的三维数组。

因为很少用到new来分配多维数组,所以在这里贴上分配空间的代码。基本思路,是先创建外围的数组,再创建内围的数组。

1     uchar*** patches;
2     patches = new uchar** [rows];
3     for (int i = 0; i < rows; i++) {
4         patches[i] = new uchar* [cols];
5         for (int j = 0; j < cols; j++) {
6             patches[i][j] = new uchar [w * h];
7         }
8     }

之后的访问就直接用数组下标。运行之后,发现时间节省了很多,用时只需要30-40秒。所以应该还是每一个循环都创建了新的vector浪费了时间,如果在循环之前创建好三个vector,每一次处理完一行的块就清理掉重复利用,应该也会节省到这个时间段里。不过我认为还是定长的数组时间会少些,所以暂时不再实验vector的改进。

另外要注意的就是使用完数组后,需要进行delete操作,同样也是需要用两层循环来从内到外delete。

 

做完这个题目,感觉还是逻辑思维搞不清楚,虽然思考的时候有画例子比比划划,但是明明已经靠近结果了、却没办法凭一己之力到达终点,还是需要变换思维的角度,从多个角度去看问题。另外还要懂得将基础的知识扩展到实际应用中,比如说这一次的多维数组空间分配,竟然一开始还在疑惑为什么第二行的类型是uchar**……

还是不能把学过的东西丢掉啊。

 

以上内容参考了这几个博客,感谢他们的分享!

http://www.lewensky.cn/read.php/186.htm

http://pppboy.blog.163.com/blog/static/302037962010218103838998/

posted @ 2014-12-09 15:22  吊车尾也要努力学习  阅读(1191)  评论(0编辑  收藏  举报