1 ROI概念
ROI是region of interest首字母的簡寫,翻譯爲感性趣的區域,其對象時圖像。
對於圖像,其實就是一個二維數組,只不過這個二維數組有點特殊,它有頭信息,在頭信息裏會有描述這個二維數組的大小、圖片類型和數組元素的數據類型等。下面是一張從官方教程裏獲取的一張輔助理解的圖片。
上面的圖片只是一張灰度圖,而我們常見的基本都是彩色圖,在灰度圖中一個像素我們用一個值就可以表示了,但在彩色的圖片中一個像素要用3個值或4個值(有的圖片有alpha通道)來表示。下面就是一個由三個值表示一個像素的輔助理解圖(BGR格式的圖片)。
容易發現,其顏色排列時BGR順序,其實在OpenCV中很多圖片都是BGR格式存儲,和我們常見的到RGB格式的存儲方式是相反的。其他圖片的格式有很多種,如HVS、CyCbCr、HSI等格式。
2 爲什麼要設置ROI
圖片是一個二維數組,而一般情況下,我們處理圖像時只對其中的部分區域進行處理,例如我們想在圖片某個區域打馬賽克,爲了性能考慮我們,可以只讓程序對這部分信息進行處理而將其他部分忽略,這時我們就要設置圖片感性趣的區域。設置完感性趣的區域後,其實是指針指到了ROI區域的左上角,好像我們截取了一張小圖片一樣,我們只對這張小圖片進行處理就可以了,因其ROI指向的還是原圖只在告訴它圖片的起始位置和大小變了,所以在對ROI區操作會影響原圖。
3 設置ROI
在OpenCV中有C和C++的代碼,最早OpenCV是用C寫的,在開發中C的代碼寫起來不太方便在版本進入2.0之後後續加入的代碼改用C++,所以設置ROI的方法有兩種即C和C++的,C的已不常用不過這裏還會列出已方便了解。
C++
(void)setImageROI:(cv::Mat)image{
// 設置ROI
// 方法一
cv::Mat roiImage = image(cv::Rect(100, 100, 200, 100));
// 方法二,第一個range表示起始行和終止行,第二個range是起始列和終止列
//cv::Mat roiImage = testImage(cv::Range(100, 100 + 100), cv::Range(100, 200 + 100));
// 畫一個矩形
cv::rectangle(roiImage, cv::Rect(0, 0, 200, 100), cv::Scalar(255, 0, 0), 10);
}
設置ROI其實就是在原來圖片上指定一個區域,而這個區域只是新創建了一個圖片文件的頭信息而已並沒有產生新的圖片,文件頭裏的圖片區域的起始位置指向了ROI區域的左上角位置,所以在ROI上做的任何操作都會影響原圖片。
C
(void)setImageROI:(IplImage *)image{
// 記錄圖片的大小和區域
CvRect currentRect = cvGetImageROI(image);
// 設置ROI區域
cvSetImageROI(image, cvRect(100, 100, 200, 100));
// 畫一個矩形
cvRectangleR(image, cvRect(0, 0, 200, 100), CvScalar(255, 0, 0), 10);
// 還原ROI區域
cvSetImageROI(image, currentRect);
// 上面的還原ROI區域要一個臨時變量,也可通過下面的方法,還原ROI區域而不用創建臨時變量
//cvResetImageROI(image);
}
C沒有生成一個文件頭信息而是修改原來的文件頭信息,所以要把文件頭信息改回去。相比而言C++比C更加方便。
4 實例分析
一、設置ROI並畫矩形
頭文件:
#import <opencv2/opencv.hpp>
#import <opencv2/imgproc/types_c.h>
#import <opencv2/imgcodecs/ios.h>
下面是核心ROI代碼
(UIImage *)getOpenCVImage{
// 獲取測試用的圖片路徑
NSString * path = [[NSBundle mainBundle] pathForResource:@"test" ofType:nil];
// 讀取圖片
cv::Mat testImage = cv::imread([path cStringUsingEncoding:NSUTF8StringEncoding]);
// 設置ROI
cv::Mat roiImage = testImage(cv::Rect(100, 100, 100, 100));
// 在ROI區域做操作,畫一個矩形
cv::rectangle(roiImage, cv::Rect(5, 5, 50, 50), cv::Scalar(255, 255, 255), 10);
// 將圖片的格式從BGR轉換成RGB,如果不轉會造成顯示的圖片顏色出錯
cv::cvtColor(testImage, testImage, cv::COLOR_BGR2RGB);
// 返回UIImage類型的圖片
return MatToUIImage(testImage);
}
運行結果如下第一張是原圖,第二張是處理後的圖片,可以看到我們在ROI的(0,0)位置開始畫矩形,但在大圖中實際效果卻不在左上角,這就是設置ROI的效果。
原始圖
運行結果
超出ROI的效果
我們將上面代碼改成正以下代碼,讓畫矩形區域的高度大於ROI的高度。
// 放大矩形的高度,讓其超出ROI的區域
cv::rectangle(roiImage, cv::Rect(0, 0, 200, 150), cv::Scalar(255, 0, 0), 10);
超出ROI操作的效果
可以看出,超出ROI的操作是被丟棄的。
二、設置ROI實現圖片移位
上面說過設置ROI後我們就可以只對該區域進行操作。我們練習一下將一ROI區域的數據放到另一個ROI區域。
- (UIImage *)getOpenCVImage{
// 獲取測試用的圖片路徑
NSString * path = [[NSBundle mainBundle] pathForResource:@"test" ofType:nil];
// 讀取圖片
cv::Mat testImage = cv::imread([path cStringUsingEncoding:NSUTF8StringEncoding]);
// 設置ROI區域A
cv::Mat roiImageA = testImage(cv::Rect(100, 100, 200, 100));
// 設置ROI區域B
cv::Mat roiImageB = testImage(cv::Rect(300, 30, 200, 100));
// 將roiImageB數據放到roiImageA的區域以實現圖片區域移動效果
roiImageB.copyTo(roiImageA);
// 將圖片的格式從BGR轉換成RGB,如果不轉會造成顯示的圖片顏色出錯
cv::cvtColor(testImage, testImage, cv::COLOR_BGR2RGB);
// 將圖片轉成UIImage並返回
return MatToUIImage(testImage);
}
代碼運行後結果:
實現圖片區域移動