日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

OpenCV下车牌定位算法实现代码

發(fā)布時(shí)間:2023/11/27 生活经验 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV下车牌定位算法实现代码 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)自:http://blog.csdn.net/heihei723/archive/2006/05/14/728046.aspx#FeedBack

?

車牌定位算法在車牌識(shí)別技術(shù)中占有很重要地位,一個(gè)車牌識(shí)別系統(tǒng)的識(shí)別率往往取決于車牌定位的成功率及準(zhǔn)確度。

????? 車牌定位有很多種算法,從最簡(jiǎn)單的來(lái),車牌在圖像中一般被認(rèn)為是長(zhǎng)方形,由于圖像攝取角度不同也可能是四邊形。我們可以使用OpenCV中的實(shí)例: C:/Program Files/OpenCV/samples/c.squares.c 這是一個(gè)搜索圖片中矩形的一個(gè)算法。我們只要稍微修改一下就可以實(shí)現(xiàn)定位車牌。

????? 在這個(gè)實(shí)例中使用了canny算法進(jìn)行邊緣檢測(cè),然后二值化,接著用cvFindContours搜索輪廓,最后從找到的輪廓中根據(jù)角點(diǎn)的個(gè)數(shù),角的度數(shù)和輪廓大小確定,矩形位置。以下是效果圖:

?

???這個(gè)算法可以找到一些車牌位置,但在復(fù)雜噪聲背景下,或者車牌圖像灰度與背景相差不大就很難定位車牌

所以我們需要尋找更好的定位算法。下面是squares的代碼:

#ifdef _CH_
#pragma package <opencv>
#endif

#ifndef _EiC
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#endif

int thresh = 50;
IplImage* img = 0;
IplImage* img0 = 0;
CvMemStorage* storage = 0;
CvPoint pt[4];
const char* wndname = "Square Detection Demo";

// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 )
{
??? double dx1 = pt1->x - pt0->x;
??? double dy1 = pt1->y - pt0->y;
??? double dx2 = pt2->x - pt0->x;
??? double dy2 = pt2->y - pt0->y;
??? return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
CvSeq* findSquares4( IplImage* img, CvMemStorage* storage )
{
??? CvSeq* contours;
??? int i, c, l, N = 11;
??? CvSize sz = cvSize( img->width & -2, img->height & -2 );
??? IplImage* timg = cvCloneImage( img ); // make a copy of input image
??? IplImage* gray = cvCreateImage( sz, 8, 1 );
??? IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
??? IplImage* tgray;
??? CvSeq* result;
??? double s, t;
??? // create empty sequence that will contain points -
??? // 4 points per square (the square's vertices)
??? CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
???
??? // select the maximum ROI in the image
??? // with the width and height divisible by 2
??? cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));
???
??? // down-scale and upscale the image to filter out the noise
??? cvPyrDown( timg, pyr, 7 );
??? cvPyrUp( pyr, timg, 7 );
??? tgray = cvCreateImage( sz, 8, 1 );
???
??? // find squares in every color plane of the image
??? for( c = 0; c < 3; c++ )
??? {
??????? // extract the c-th color plane
??????? cvSetImageCOI( timg, c+1 );
??????? cvCopy( timg, tgray, 0 );
???????
??????? // try several threshold levels
??????? for( l = 0; l < N; l++ )
??????? {
??????????? // hack: use Canny instead of zero threshold level.
??????????? // Canny helps to catch squares with gradient shading??
??????????? if( l == 0 )
??????????? {
??????????????? // apply Canny. Take the upper threshold from slider
??????????????? // and set the lower to 0 (which forces edges merging)
??????????????? cvCanny( tgray, gray,60, 180, 3 );
??????????????? // dilate canny output to remove potential
??????????????? // holes between edge segments
??????????????? cvDilate( gray, gray, 0, 1 );
??????????? }
??????????? else
??????????? {
??????????????? // apply threshold if l!=0:
??????????????? //???? tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
??????????????? //cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
??? cvThreshold( tgray, gray, 50, 255, CV_THRESH_BINARY );
??????????? }
???????????
??????????? // find contours and store them all as a list
??????????? cvFindContours( gray, storage, &contours, sizeof(CvContour),
??????????????? CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
???????????
??????????? // test each contour
??????????? while( contours )
??????????? {
??????????????? // approximate contour with accuracy proportional
??????????????? // to the contour perimeter
??????????????? result = cvApproxPoly( contours, sizeof(CvContour), storage,
??????????????????? CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
??????????????? // square contours should have 4 vertices after approximation
??????????????? // relatively large area (to filter out noisy contours)
??????????????? // and be convex.
??????????????? // Note: absolute value of an area is used because
??????????????? // area may be positive or negative - in accordance with the
??????????????? // contour orientation
??????????????? if( result->total == 4 &&
??????????????????? fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&
??????????????????? cvCheckContourConvexity(result) )
??????????????? {
??????????????????? s = 0;
???????????????????
??????????????????? for( i = 0; i < 5; i++ )
??????????????????? {
??????????????????????? // find minimum angle between joint
??????????????????????? // edges (maximum of cosine)
??????????????????????? if( i >= 2 )
??????????????????????? {
??????????????????????????? t = fabs(angle(
??????????????????????????? (CvPoint*)cvGetSeqElem( result, i ),
??????????????????????????? (CvPoint*)cvGetSeqElem( result, i-2 ),
??????????????????????????? (CvPoint*)cvGetSeqElem( result, i-1 )));
??????????????????????????? s = s > t ? s : t;
??????????????????????? }
??????????????????? }
???????????????????
??????????????????? // if cosines of all angles are small
??????????????????? // (all angles are ~90 degree) then write quandrange
??????????????????? // vertices to resultant sequence
??????????????????? if( s < 0.3 )
??????????????????????? for( i = 0; i < 4; i++ )
??????????????????????????? cvSeqPush( squares,
??????????????????????????????? (CvPoint*)cvGetSeqElem( result, i ));
??????????????? }
???????????????
??????????????? // take the next contour
??????????????? contours = contours->h_next;
??????????? }
??????? }
??? }
???
??? // release all the temporary images
??? cvReleaseImage( &gray );
??? cvReleaseImage( &pyr );
??? cvReleaseImage( &tgray );
??? cvReleaseImage( &timg );
???
??? return squares;
}


// the function draws all the squares in the image
void drawSquares( IplImage* img, CvSeq* squares )
{
??? CvSeqReader reader;
??? IplImage* cpy = cvCloneImage( img );
??? int i;
???
??? // initialize reader of the sequence
??? cvStartReadSeq( squares, &reader, 0 );
???
??? // read 4 sequence elements at a time (all vertices of a square)
??? for( i = 0; i < squares->total; i += 4 )
??? {
??????? CvPoint* rect = pt;
??????? int count = 4;
???????
??????? // read 4 vertices
??????? memcpy( pt, reader.ptr, squares->elem_size );
??????? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
??????? memcpy( pt + 1, reader.ptr, squares->elem_size );
??????? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
??????? memcpy( pt + 2, reader.ptr, squares->elem_size );
??????? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
??????? memcpy( pt + 3, reader.ptr, squares->elem_size );
??????? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
???????
??????? // draw the square as a closed polyline
??????? cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );
??? }
???
??? // show the resultant image
??? cvShowImage( wndname, cpy );
??? cvReleaseImage( &cpy );
}


void on_trackbar( int a )
{
??? if( img )
??????? drawSquares( img, findSquares4( img, storage ) );
}

char* names[] = { "pic1.png", "pic2.png", "pic3.png",
????????????????? "pic4.png", "pic5.png", "pic6.png", 0 };

int main(int argc, char** argv)
{
??? int i, c;
??? // create memory storage that will contain all the dynamic data
??? storage = cvCreateMemStorage(0);

??? for( i = 0; names[i] != 0; i++ )
??? {
??????? // load i-th image
??????? img0 = cvLoadImage( names[i], 1 );
??????? if( !img0 )
??????? {
??????????? printf("Couldn't load %s/n", names[i] );
??????????? continue;
??????? }
??????? img = cvCloneImage( img0 );
???????
??????? // create window and a trackbar (slider) with parent "image" and set callback
??????? // (the slider regulates upper threshold, passed to Canny edge detector)
??????? cvNamedWindow( wndname,0 );
??????? cvCreateTrackbar( "canny thresh", wndname, &thresh, 1000, on_trackbar );
???????
??????? // force the image processing
??????? on_trackbar(0);
??????? // wait for key.
??????? // Also the function cvWaitKey takes care of event processing
??????? c = cvWaitKey(0);
??????? // release both images
??????? cvReleaseImage( &img );
??????? cvReleaseImage( &img0 );
??????? // clear memory storage - reset free space position
??????? cvClearMemStorage( storage );
??????? if( c == 27 )
??????????? break;
??? }
???
??? cvDestroyWindow( wndname );
???
??? return 0;
}

#ifdef _EiC
main(1,"squares.c");
#endif

?

前面介紹了用OpenCV的squares實(shí)例定位車牌的算法,效果不是很理想。車牌定位的方法有很多種,這里我們從汽車圖像的紋理特征入手,找出車牌不同于背景的特征是車牌定位的關(guān)鍵。觀察多幅汽車圖片我們會(huì)發(fā)現(xiàn)車身和背景的紋理多為橫向紋理,而車牌字符則為豎向紋理,基于這個(gè)紋理特征我們可以區(qū)別處車牌位置。車牌的定位我們可以分為如下幾個(gè)步驟:
1預(yù)處理
?? 圖像的預(yù)處理主要是為了后續(xù)處理的需要進(jìn)行一些濾波和梯度增強(qiáng)的處理,以濾除噪聲和垂直方向上的增強(qiáng)。
2 Sobel垂直方向邊緣檢測(cè)并2值化
一般的邊緣檢測(cè)的方法檢測(cè)到的邊緣信息含有大量的無(wú)用信息,這里我們使用Sobel邊緣檢測(cè)算子對(duì)圖像進(jìn)行垂直邊緣檢測(cè),由于車牌字符的豎向紋理特征我們可以檢測(cè)到車牌字符,對(duì)得到的邊緣圖像進(jìn)行2值化,排除一些噪聲并增強(qiáng)字符邊緣。
3形態(tài)學(xué)變換
汽車圖像還存在一些豎向的紋理(如車身,車燈等)我們需要排除這些干擾信息,并把臨近的字符邊緣連通起來(lái)。這一階段主要利用對(duì)圖像進(jìn)行形態(tài)學(xué)操作,即作閉合(先膨脹在腐蝕)開(kāi)啟(先腐蝕再膨脹)運(yùn)算,膨脹可以把臨近邊緣連接成一個(gè)整體,腐蝕可以濾除一些細(xì)碎的邊緣(建議使用3*1的結(jié)構(gòu)進(jìn)行操作)經(jīng)過(guò)形態(tài)學(xué)變換可以濾除噪聲邊緣,得到車牌區(qū)域。
4篩選
大多數(shù)圖像經(jīng)過(guò)形態(tài)學(xué)變化后都可以得到滿意的效果,但由于圖像背景的一些特殊紋理,可能會(huì)留下一些沒(méi)有濾除的邊緣,也有可能字符間空隙較大,車牌區(qū)域出現(xiàn)分為幾部分的狀況,還有可能車身的一些標(biāo)語(yǔ)等被誤認(rèn)為是車牌圖像等,我們還需要進(jìn)行聚類和篩選。
選取一個(gè)標(biāo)準(zhǔn)來(lái)判斷連通域(如豎直方向兩區(qū)域重合大于10像素,水平方向兩區(qū)域距離小于10像素)在根據(jù)車牌的形狀大小(寬,高)判斷是否為車牌區(qū)。
程序運(yùn)行效果圖如下:

?

總結(jié)

以上是生活随笔為你收集整理的OpenCV下车牌定位算法实现代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

歡迎分享!

轉(zhuǎn)載請(qǐng)說(shuō)明來(lái)源于"生活随笔",并保留原作者的名字。

本文地址:OpenCV下车牌定位算法实现代码