opencv 处理黑夜_黑夜给我了黑色眼睛:OpenCV识别黑色矩形

发布于:2021-10-22 04:27:22

在计算机图形领域,识别特定形状是一项基本功能,但从初学者的角度来说,这一切并不容易,接下来我将为大家详细介绍如何利用OpenCV实现简单的颜色+形状识别。


一、目标介绍。


实现一个这样的程序,能够将识别摄像头的实时视频流中黑色矩形,并将其标记出来


二、思路分解以及代码实现。


具体实现思路是这样的:


1、抓取电脑摄像头的实时视频流,将其一帧用Mat类存储起来,利用while循环就可以将视屏流一帧帧地放入Mat,然后针对每一帧的图片进行处理。这样我们就将处理视频的任务转化为处理图片。


while(1)


{


Mat video;


capture >> video;


}


2、将图片(Mat Video)中黑色的区域分离出来,用一个二值图(值只有0和255,即黑和白)来储存黑色区域的信息,其中值为255(显示为白色)的点代表黑色。具体的执行细节如下:


a、将图片由RGB色域转化为HSV色域。为什么?要知道,光照情况会影响颜色,比如白色的物体在光线*凳保岜坏缒允侗鹞谏6褂肏SV色域可以极大地减少光照对颜色识别的影响。OpenCV提供了cvtColor()这个函数处理此情况。


Mat hsv;


cvtColor(frame,hsv,COLOR_BGR2HSV);


b、利用inRange()函数分离出图片的黑色部分


Mat blkobj;


inRange(hsv,Scalar(0,0,0),Scalar(180,255,46),blkobj);


图片的黑色部分就已经储存在了Mat blkobj 里。


3、图片的黑色区域已经得到了,那么我们该如何在其中找出矩形呢?


这里提供两个思路


思路一:化繁为简,不妨先在图片中找出直线,如果存在直线能够拼成一个矩形,那么我们就判定图片中存在矩形,并把它标记出来。


思路二:具体分三个步骤


I、找出图片中最大的黑色物体(当然,你也可以选择把每个黑色物体都找出来)


II、找出其最小的外接矩形,然后求出最大黑色物体的面积与最小外接矩形面积的比值


III、如果比值足够接*1,说明这个黑色物体是矩形


思路一网上已经有人实现过了,但似乎只适用于识别简单的图片,用于视频识别的效果不佳。小编也自己实现了一遍,发现该思路存在着许多问题:


问题一:使用霍夫变换HoughLIne()寻找直线,效果不佳,对画面质量敏感。


问题二:算法实现较为复杂,小编实现出来后,未经优化算法的复杂度为O(n^4),如果线的数量n一多,视频就会卡死。


问题三:找出来的直线即使拼成矩形,也不一定是黑色矩形所在的位置。


思路一问题太多,果断放弃。


思路二也存在一些问题待解决,但实现的可能还是较高的。


问题一:如何找出图片中最大的黑色物体?(或者说,如何将图片的黑色物体一个个分离出来?)


小编利用的方法是:寻找连通域


什么意思呢?


黑色物体在识别出来的二值图上是这样的:Mat blkobj


也就是说,一个黑色物体二值图上表现为:互邻,且值为255的点。


具体的代码实现如下,作用是:一旦找到一个值为255的点,那么就从上下左右进行搜寻,搜寻一个标记一个(这样就可以不走回头路了)直到无法搜寻为止,期间将所有值为255的点都放在一个vector Chunk(元素为二维点的一维数组) 中。这样一来,每个Chunk(数组)就代表着一个黑色物体。


void MazeTrack(int r,int c)//search for the Biggest connected area{


Maze.at(r,c)=0;


Point *p=new Point(c,r);//Attention! int Point class, x means cols, y means rows; Chunk.push_back(*p);


delete p;


//** if adjacent point is not black or unmarked, end the function. if(Maze.at(r-1,c)==0&&Maze.at(r+1,c)==0&&Maze.at(r,c-1)==0&&Maze.at(r,c+1)==0) return;


//** if any of adjacent point is black and unmarked, recurse. if((r-1)>=0 && Maze.at(r-1,c)==255)//up(x-1,y) {


MazeTrack(r-1,c);


}


if((r+1)(r+1,c)==255)//down(x+1,y) {


MazeTrack(r+1,c);


}


if((c-1)>=0 && Maze.at(r,c-1)==255)//left(x,y-1) {


MazeTrack(r,c-1);


}


if((c+1)(r,c+1)==255)//right(x,y+1) {


MazeTrack(r,c+1);


}


}


问题二:给定一些点,如何寻找其最小外接矩形?


这真是一个不简单的问题,不过好在OpenCV就有函数能帮我们解决:


//** Use RotatedRect find the minAreaRect of ChunkMax Point2f vertex[4];


RotatedRect box=minAreaRect(ChunkMax);


box.points(vertex);//the vertex of rectangle is stored in vertex[4]


vertex[4]里放着最小外接矩形的4个顶点。


好啦,剩下的问题就简单多了


问题三:求面积之比。


对于最大的黑色物体,其面积就是像素的个数,即Chunk.size()。对于最小外接矩形,其面积......我不知道怎么求,不过好在不需要我教你对吧。


问题四:画出矩形。


利用line()函数具体实现看代码。


好啦,思路理清了,咱们看代码吧:


三、完整代码


PS:由于Mazetrack()这个函数是针对像素点的递归函数,在分辨率过高的时候,递归次数惊人,程序会异常退出。故小编在数据处理时将分辨率都除以了6,最后标记矩形的时候又将这个6乘了回来。所以,这个算法是有待进一步优化的。


PS:注释应该是够详尽的,更多建议请留言。


#include #include #include #include #include using namespace std;


using namespace cv;


Mat Maze;


vectorChunk;


void MazeTrack(int r,int c);


int main(int argc, const char * argv[]) {


VideoCapture capture(0);


while(1)


{


//My Code Chunk#if 1 //** capture the video Mat video;


capture >> video;


Mat frame;


resize(video, frame, Size(video.cols/6,video.rows/6));


imshow("Original", frame);


//** convert to HSV Mat hsv;


cvtColor(frame,hsv,COLOR_BGR2HSV);


//** Find the BLACK Area Mat blkobj;


Mat element = getStructuringElement(MORPH_RECT, Size(3,3 ));


//morphologyEx(hsv, hsv, MORPH_OPEN,element); //morphologyEx(hsv, hsv, MORPH_CLOSE,element); inRange(hsv,Scalar(0,0,0),Scalar(180,255,46),blkobj);


imshow("BlackArea", blkobj);


//** Find the BIGGEST black area Maze=blkobj.clone();


vector >Chunks;


vectorChunkMax;


int rows = Maze.rows;


int cols = Maze.cols;


for(int i = 0; i < rows; i++)


{


for(int j = 0; j < cols; j++)


{


if(Maze.at(i,j)==255)//at<>(rows,cols); {


MazeTrack(i,j);


Chunks.push_back(Chunk);


Chunk.clear();


vector ().swap(Chunk);


}


}


}


long max=0;


for(int n=0;n


{


if(max


{


max=Chunks[n].size();


ChunkMax.swap(Chunks[n]);


}


}


Chunks.clear();


vector >().swap(Chunks);


//** Use RotatedRect find the minAreaRect of ChunkMax Point2f vertex[4];


RotatedRect box=minAreaRect(ChunkMax);


box.points(vertex);//the vertex of rectangle is stored in vertex[4]


//** use the coordinate of vertex to get the high and the length of minAreaRectangle double rectl=sqrt(pow(vertex[0].x-vertex[1].x,2)+pow(vertex[0].y-vertex[1].y,2));


double recth=sqrt(pow(vertex[1].x-vertex[2].x,2)+pow(vertex[1].y-vertex[2].y,2));


double area=recth*rectl;


//** if the ratio of ChunkMax.size() : Area of rectangle is high enough, judge it to be rectangle double ratio=double(ChunkMax.size())/area;


cout<


if(ratio>=0.75)


{


for(int i=0;i<4;i++)


{


line(video,6*vertex[i],6*vertex[(i+1)%4],Scalar(255,0,0),2,8);


}


}


resize(frame, frame, Size(frame.cols*2,frame.rows*2));


imshow("Rect", video);


waitKey(50);


#endif }


}


void MazeTrack(int r,int c)//search for the Biggest connected area{


Maze.at(r,c)=0;


Point *p=new Point(c,r);//Attention! int Point class, x means cols, y means rows; Chunk.push_back(*p);


delete p;


//** if adjacent point is not black or unmarked, end the function. if(Maze.at(r-1,c)==0&&Maze.at(r+1,c)==0&&Maze.at(r,c-1)==0&&Maze.at(r,c+1)==0) return;


//** if any of adjacent point is black and unmarked, recurse. if((r-1)>=0 && Maze.at(r-1,c)==255)//up(x-1,y) {


MazeTrack(r-1,c);


}


if((r+1)(r+1,c)==255)//down(x+1,y) {


MazeTrack(r+1,c);


}


if((c-1)>=0 && Maze.at(r,c-1)==255)//left(x,y-1) {


MazeTrack(r,c-1);


}


if((c+1)(r,c+1)==255)//right(x,y+1) {


MazeTrack(r,c+1);


}


}


四、效果展示https://www.zhihu.com/video/920698725767188480


五、更大的世界


你们喜欢看书嘛~爱书人士快快关注小编的读书公众号:林木蔚然读书会








相关资源:OpenMV形状识别And颜色识别.py

相关推荐

最新更新

猜你喜欢