OpenCV2.4.13 中 選取roi區域,任意形狀(矩形,不規則多邊形,圓形,橢圓,手動指定形狀)
阿新 • • 發佈:2019-02-12
在利用OpenCV對影象進行處理時,通常會遇到一個情況,就是隻需要對部分感興趣區域進行處理。
因此,如何選取感興趣區域(其實就是“摳圖”)。
下面給出一個例子:
Mat img = imread(IMG_PATH);
Mat cat = imread(CAT_PATH);
if (img.empty()|| cat.empty())
cerr << "can not read image."<<endl;
// 指定感興趣區域,兩種方法
Mat ROI = img(Rect(40,40,cat.cols,cat.rows ));
Mat ROI2(img,Rect(40,40,cat.cols,cat.rows));
// 展示 roi 區域
imshow("roi",ROI);
cout<<endl
<<"將貓放到感興趣區域,兩種方法"<<endl;
//cat.copyTo(ROI);
cat.copyTo(ROI,cat);
imshow("lotus with cat",img);
// 在影象中畫出 矩形
rectangle(img,Rect(240,240,cat.cols,cat.rows ),Scalar(0,0,255));
imshow("with rectangle box",img);
// 另一種方法
cout <<endl
<< "利用 Rect 儲存方框,然後使用"<<endl;
Rect r1 = Rect(100,0,200,200);
rectangle(img,r1,Scalar(255,0,0));
imshow("with rectangle box 2",img);
下面是程式最終的結果:
- 問題:如果感興趣區域不是方形的怎麼辦?
答:參考這裡的程式碼,也就是說,使用 contour (輪廓)來指定roi。
其程式碼如下:
Mat img = imread(IMG_PATH);
Mat dst;
Mat roi = Mat::zeros(img.size(),CV_8U);
vector<vector<Point>> contour;
vector<Point> pts;
pts.push_back(Point(30,45));
pts.push_back(Point(100,15));
pts.push_back(Point(300,145));
pts.push_back(Point(330,240));
pts.push_back(Point(50,250));
contour.push_back(pts);
drawContours(roi,contour,0,Scalar::all(255),-1);
img.copyTo(dst,roi);
imshow("roi",roi);
imshow("img",img);
imshow("dst",dst);
效果圖:
- 問題:我不想要這麼複雜的區域,只是想要一個圓形區域呢?
答:經過參考這裡的回答,給出其中回答中的程式碼,可以畫出圓形區域的roi。
Mat image = imread(IMG_PATH);
Mat dst = Mat::zeros(image.size(), image.type());
Mat mask = Mat::zeros(image.size(),CV_8U);
Point circleCenter(mask.cols / 2, mask.rows / 2);
int radius = min(mask.cols, mask.rows)/2;
// 畫圓
circle(mask, circleCenter, radius, Scalar(255),-1);
image.copyTo(dst, mask);
imshow("mask",mask);
imshow("image",image);
imshow("dst",dst);
效果如下:
- 問題:我想要個橢圓區域呢?
答:將上面程式碼中畫圓的那一句替換為:
ellipse(mask,circleCenter,Size(240,146),10,-180,180,Scalar(255),-1);
效果如下:
- 問題:說了這麼多,有什麼規律麼?
答:有啊。
其實主要用到了一個函式:copyTo,先看手冊中 它的定義:
給出一個例子:
src.copyTo(dst, mask);
這裡解釋一下:將 src 的位於 mask 中的部分,拷貝到 dst 中。
這裡,mask是一個“掩膜”, 其中非零的位置既是指定了 src 中的那些需要拷貝的部分。
上面才是整個方法的核心部分。
- 問題:我想手動,用滑鼠選取感興趣區域,怎麼辦麼?
答:額,這個我還沒用到,不過幫你搜到了一個相關的部落格,在這裡。具體效果如何,我沒有實驗,實在需要的話,可以自己折騰一下。
不過,這裡貌似只能手動選擇方形區域。
放大招:整體程式碼如下:
// csdn_code.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
//#define IMG_PATH "..//figures//12.jpg"
#define IMG_PATH "..//figures//lotus.jpg"
#define CAT_PATH "..//figures//cat.jpg"
void testroi(void){
Mat img = imread(IMG_PATH);
Mat cat = imread(CAT_PATH);
if (img.empty()|| cat.empty())
cerr << "can not read image."<<endl;
// 指定感興趣區域,兩種方法
Mat ROI = img(Rect(40,40,cat.cols,cat.rows));
Mat ROI2(img,Rect(40,40,cat.cols,cat.rows));
// 展示 roi 區域
imshow("roi",ROI);
cout<<endl
<<"將貓放到感興趣區域,兩種方法"<<endl;
//cat.copyTo(ROI);
cat.copyTo(ROI,cat);
imshow("lotus with cat",img);
// 在影象中畫出 矩形
rectangle(img,Rect(240,240,cat.cols,cat.rows),Scalar(0,0,255));
imshow("with rectangle box",img);
// 另一種方法
cout <<endl
<< "利用 Rect 儲存方框,然後使用"<<endl;
Rect r1 = Rect(100,0,200,200);
rectangle(img,r1,Scalar(255,0,0));
imshow("with rectangle box 2",img);
}
void contour_roi(void){
Mat img = imread(IMG_PATH);
Mat dst;
Mat roi = Mat::zeros(img.size(),CV_8U);
// 利用 邊界設定roi區域
vector<vector<Point>> contour;
vector<Point> pts;
pts.push_back(Point(30,45));
pts.push_back(Point(100,15));
pts.push_back(Point(300,145));
pts.push_back(Point(330,240));
pts.push_back(Point(50,250));
contour.push_back(pts);
// 畫出
drawContours(roi,contour,0,Scalar::all(255),-1);
img.copyTo(dst,roi);
imshow("roi",roi);
imshow("img",img);
imshow("dst",dst);
}
void circle_roi(void){
Mat image = imread(IMG_PATH);
Mat dst = Mat::zeros(image.size(), image.type());
Mat mask = Mat::zeros(image.size(),CV_8U);
Point circleCenter(mask.cols / 2, mask.rows / 2);
int radius = min(mask.cols, mask.rows)/2;
// 畫圓
//circle(mask, circleCenter, radius, Scalar(255),-1);
// 畫橢圓
ellipse(mask,circleCenter,Size(240,146),10,-180,180,Scalar(255),-1);
image.copyTo(dst, mask);
imshow("mask",mask);
imshow("image",image);
imshow("dst",dst);
}
int main()
{
testroi();
contour_roi();
circle_roi();
waitKey();
system("pause");
return 0;
}