OpenCV Mat遍历的方法
OpenCV像素遍歷常用的是三種方法:ptr指針,迭代器(iterator)以及動態地址at。
- 動態地址at不適合用于像素遍歷,速度太慢了,比較適合隨機訪問的方式;
- 使用Mat的ptr指針進行圖像遍歷更加高效,
特別的:一般圖像行與行之間往往存儲是不連續的,但是有些圖像可以是連續的,Mat提供了一個檢測圖像是否連續的函數isContinuous()。當圖像連通時,我們就可以把圖像完全展開,看成是一行進行處理。
因此最高效的遍歷方法如下:
void image_copy(cv::Mat &src, cv::Mat &dst) {int h = src.rows;int w = src.cols;dst.create(src.size(), src.type());if (src.isContinuous() && dst.isContinuous()) {h = 1;w = w * src.rows * src.channels();}for (int i = 0; i < h; i++) {uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {*dptr++ = *sptr++;//dptr[j] = sptr[j];}} }?PS:一般經過裁剪的Mat圖像,都不再連續了,如cv::Mat crop_img = src(rect);crop_img 是不連續的Mat圖像,如果想轉為連續的,最簡單的方法,就是將不連續的crop_img 重新clone()一份給新的Mat就是連續的了。關于Mat連續存儲的問題,可見:http://blog.csdn.net/guyuealian/article/details/78614662
?
其他遍歷方式 ,可參考:
實現方式: https://blog.csdn.net/keith_bb/article/details/53071133
void image_copy1(cv::Mat &src, cv::Mat &dst) {//使用ptr指針int n = src.channels();int h = src.rows; //獲取圖像矩陣行數int w = src.cols; //獲取圖像矩陣列數dst.create(src.size(), src.type()); //初始化返回結果for (int i = 0; i < h; i++) {//獲取矩陣每行首地址指針uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {uchar b = sptr[n * j];uchar g = sptr[n * j + 1];uchar r = sptr[n * j + 2];dptr[n * j] = b;dptr[n * j + 1] = g;dptr[n * j + 2] = r;}} }void image_copy2(cv::Mat &src, cv::Mat &dst) {//使用ptr指針int n = src.channels();int h = src.rows; //獲取圖像矩陣行數int w = src.cols * n; //三通道圖像每行元素個數為列數x通道數dst.create(src.size(), src.type());for (int i = 0; i < h; i++) {//獲取矩陣每行首地址指針uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {dptr[j] = sptr[j];}} }void image_copy3(cv::Mat &src, cv::Mat &dst) {//使用ptr指針int n = src.channels();int h = src.rows; //獲取圖像矩陣行數int w = src.cols * n; //三通道圖像每行元素個數為列數x通道數dst.create(src.size(), src.type());for (int i = 0; i < h; i++) {//獲取矩陣每行首地址指針uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j += n) {//uchar b = sptr[j];//uchar g = sptr[j + 1];//uchar r = sptr[j + 2];uchar b = *sptr++;uchar g = *sptr++;uchar r = *sptr++;dptr[j] = b;dptr[j + 1] = g;dptr[j + 2] = r;}} }void image_copy(cv::Mat &src, cv::Mat &dst) {int h = src.rows;int w = src.cols;dst.create(src.size(), src.type());if (src.isContinuous() && dst.isContinuous()) {h = 1;w = w * src.rows * src.channels();}for (int i = 0; i < h; i++) {uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {*dptr++ = *sptr++;//dptr[j] = sptr[j];}} }
以下是圖像融合的算法:
(1)這是完全使用OpenCV的接口實現的圖像融合,循環5次耗時30.29811 ms
void image_fusion(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {if (matte.channels() == 1) {matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);cv::cvtColor(matte, matte, cv::COLOR_GRAY2BGR);} else {matte.convertTo(matte, CV_32FC3, 1.0 / 255, 0);}//out = imgBGR.clone();vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);bg.convertTo(bg, CV_32FC3, 1, 0);imgBGR.convertTo(out, CV_32FC3, 1, 0);// Fix a Bug: 1 - alpha實質上僅有B通道參與計算,多通道時(B,G,R),需改Scalar(1.0, 1.0, 1.0)-alpha// out = out.mul(alpha) + bgi.mul(1 - alpha);out = out.mul(matte) + bg.mul(cv::Scalar(1.0, 1.0, 1.0) - matte);out.convertTo(out, CV_8UC3, 1, 0); }(2)這是通過遍歷的方式實現的圖像融合,循環5次耗時24.44169 ms
注意到matte需要除以255,把它放在循環體外進行乘法和除法運算,可以明顯加快 void image_fusion_fast(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {assert(matte.channels() == 1);out.create(imgBGR.size(), CV_8UC3);vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);int h = imgBGR.rows;int w = imgBGR.cols;int n = imgBGR.channels();// 循環體外進行乘法和除法運算matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);for (int i = 0; i < h; ++i) {uchar *sptr = imgBGR.ptr<uchar>(i);uchar *dptr = out.ptr<uchar>(i);float *mptr = matte.ptr<float>(i);uchar *bptr = bg.ptr<uchar>(i);for (int j = 0; j < w; ++j) {//float alpha = mptr[j] / 255; //循環體盡量減少乘法和除法運算float alpha = mptr[j];float _alpha = 1.f - alpha;dptr[n * j] = uchar(sptr[n * j] * alpha + bptr[n * j] * _alpha);dptr[n * j + 1] = uchar(sptr[n * j + 1] * alpha + bptr[n * j + 1] * _alpha);dptr[n * j + 2] = uchar(sptr[n * j + 2] * alpha + bptr[n * j + 2] * _alpha);}} }(3)如果輸入的Mat都是連續存儲的,則可以轉換為向量的形式進行遍歷,循環5次耗時23.10372ms
void image_fusion_fast(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {assert(matte.channels() == 1);out.create(imgBGR.size(), CV_8UC3);vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);int n = imgBGR.channels();int h = imgBGR.rows;int w = imgBGR.cols * n;// 循環體外進行乘法和除法運算matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);for (int i = 0; i < h; ++i) {uchar *sptr = imgBGR.ptr<uchar>(i);uchar *dptr = out.ptr<uchar>(i);float *mptr = matte.ptr<float>(i);uchar *bptr = bg.ptr<uchar>(i);for (int j = 0; j < w; j += n) {//float alpha = mptr[j] / 255; //循環體盡量減少乘法和除法運算float alpha = mptr[j / 3];float _alpha = 1.f - alpha;dptr[j] = uchar(sptr[j] * alpha + bptr[j] * _alpha);dptr[j + 1] = uchar(sptr[j + 1] * alpha + bptr[j + 1] * _alpha);dptr[j + 2] = uchar(sptr[j + 2] * alpha + bptr[j + 2] * _alpha);}} }(3)這是通過遍歷的方式,去除了循環體內大部分乘法,實現的圖像融合,循環5次耗時23.10372ms
void image_fusion_fast(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {assert(matte.channels() == 1);out.create(imgBGR.size(), CV_8UC3);vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);int n = imgBGR.channels();int w = imgBGR.cols * imgBGR.rows * n;// 循環體外進行乘法和除法運算matte.convertTo(matte, CV_32FC3, 1.0 / 255, 0);uchar *sptr = imgBGR.ptr<uchar>(0);uchar *dptr = out.ptr<uchar>(0);float *mptr = matte.ptr<float>(0);uchar *bptr = bg.ptr<uchar>(0);for (int j = 0; j < w; j += n) {//float alpha = mptr[j] / 255; //循環體盡量減少乘法和除法運算float alpha = mptr[j / 3];float _alpha = 1.f - alpha;dptr[j] = uchar(sptr[j] * alpha + bptr[j] * _alpha);dptr[j + 1] = uchar(sptr[j + 1] * alpha + bptr[j + 1] * _alpha);dptr[j + 2] = uchar(sptr[j + 2] * alpha + bptr[j + 2] * _alpha);} }總結
以上是生活随笔為你收集整理的OpenCV Mat遍历的方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: /usr/lib/deepin-wine
- 下一篇: 深度学习端上部署工具