使用OpenCV過程中,cv::Mat比IplImage更容易操作,也符合C++使用者的習慣。但是一般Mat的數據並不是字節對齊的,對於需要字節對齊數據的函數(比如控件上的位圖顯示)來說,就會產生相應的問題。下面介紹將Mat數據轉換為字節對齊的uchar數據的方法,以三通道圖像為例,代碼如下:
- // 這里 frame 為三通道圖像
- cv::Mat roiImg;
- frame.rowRange(frame.rows/2, frame.rows).
- colRange(frame.cols/4, frame.cols*3/4).
- copyTo(roiImg); // 提取ROI區域
- int widthStep = (roiImg.cols*roiImg.elemSize()+3)/4*4; // 補齊行字節數,使它能夠被4整除 4字節對齊
- uchar *frameData = (uchar *)calloc(roiImg.rows*widthStep, sizeof(uchar)); // 申請內存
- memset(frameData, 0, roiImg.rows*widthStep);
- // 逐一復制數據
- uchar *p1, *p2;
- for (int i = 0; i < roiImg.rows; i++)
- {
- p1 = roiImg.data + i*roiImg.cols*roiImg.channels();
- p2 = frameData + i * widthStep;
- for (int j = 0; j < roiImg.cols; j++)
- {
- *(p2) = *(p1);
- *(p2+1) = *(p1+1);
- *(p2+2) = *(p1+2);
- p1 += 3;
- p2 += 3;
- }
- }
附加說明:
1、對應IplImage的cvLoadImage函數加載的圖片數據是字節對齊的,而直接將cv::Mat轉換為IplImage類型,並不會將字節對齊,只是加了個文件頭而已。
2、我在寫代碼的過程中,還發現另一個問題(與主題無關),提取ROI區域時,如果采用:
- cv::Mat roiImg = frame(cv::Rect(10, 10, 50, 50));
這樣的拷貝為淺拷貝,對后續采用指針引用逐個復制數據的過程中,實際訪問的是原frame的數據。roiImg的許多參數都仍為frame的參數,並沒有相應的改變,容易引發錯誤。這里還是采用copyTo這樣的深拷貝比較穩妥。這是實驗過程中發現的問題,也困擾了我很長時間。記錄下來,以防下次出錯。