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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

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

發布時間:2023/11/27 生活经验 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV下车牌定位算法实现代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自:http://blog.csdn.net/heihei723/archive/2006/05/14/728046.aspx#FeedBack

?

車牌定位算法在車牌識別技術中占有很重要地位,一個車牌識別系統的識別率往往取決于車牌定位的成功率及準確度。

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

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

?

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

所以我們需要尋找更好的定位算法。下面是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實例定位車牌的算法,效果不是很理想。車牌定位的方法有很多種,這里我們從汽車圖像的紋理特征入手,找出車牌不同于背景的特征是車牌定位的關鍵。觀察多幅汽車圖片我們會發現車身和背景的紋理多為橫向紋理,而車牌字符則為豎向紋理,基于這個紋理特征我們可以區別處車牌位置。車牌的定位我們可以分為如下幾個步驟:
1預處理
?? 圖像的預處理主要是為了后續處理的需要進行一些濾波和梯度增強的處理,以濾除噪聲和垂直方向上的增強。
2 Sobel垂直方向邊緣檢測并2值化
一般的邊緣檢測的方法檢測到的邊緣信息含有大量的無用信息,這里我們使用Sobel邊緣檢測算子對圖像進行垂直邊緣檢測,由于車牌字符的豎向紋理特征我們可以檢測到車牌字符,對得到的邊緣圖像進行2值化,排除一些噪聲并增強字符邊緣。
3形態學變換
汽車圖像還存在一些豎向的紋理(如車身,車燈等)我們需要排除這些干擾信息,并把臨近的字符邊緣連通起來。這一階段主要利用對圖像進行形態學操作,即作閉合(先膨脹在腐蝕)開啟(先腐蝕再膨脹)運算,膨脹可以把臨近邊緣連接成一個整體,腐蝕可以濾除一些細碎的邊緣(建議使用3*1的結構進行操作)經過形態學變換可以濾除噪聲邊緣,得到車牌區域。
4篩選
大多數圖像經過形態學變化后都可以得到滿意的效果,但由于圖像背景的一些特殊紋理,可能會留下一些沒有濾除的邊緣,也有可能字符間空隙較大,車牌區域出現分為幾部分的狀況,還有可能車身的一些標語等被誤認為是車牌圖像等,我們還需要進行聚類和篩選。
選取一個標準來判斷連通域(如豎直方向兩區域重合大于10像素,水平方向兩區域距離小于10像素)在根據車牌的形狀大小(寬,高)判斷是否為車牌區。
程序運行效果圖如下:

?

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

歡迎分享!

轉載請說明來源于"生活随笔",并保留原作者的名字。

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