多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > 身份證號碼圖像提取--基于canny邊緣檢測的連通域檢測算法

身份證號碼圖像提取--基于canny邊緣檢測的連通域檢測算法

來源:程序員人生   發布時間:2016-06-13 11:46:19 閱讀次數:5239次

在之前掃描2維碼提取任務以后,工作中又需要將身份證圖象中的身份證號碼提取出來,然后給同事調用進行辨認。之前的連通域檢測算法比較“蠻力”,由于它1旦檢測出1個大的區域,那末這區域中的所有內部區域都將不復存在了。所以在連通域檢測時,需要第1步去掉周圍可能存在的白邊,否則就會失敗。后來筆者換了1個思路,如果檢測1個區域時保存對應生成該區域的點,該區域不符合要求的話就將這些點擦掉,從而就不會影響到內部的區域了。因而就有了1下算法的誕生:

(1)從左上角開始,從碰到的第1個白點開始探測最大的連通域,獲得離該點小于max_dis的所有點,放到1個list中。

(2)然后遍歷該列表,并將離每個點距離小于max_dis的點都放到該list中。

(3)遍歷結束后,計算包括list中所有點的最小rect區域。

(4)根據設定的目標區域特點,如長寬、長寬比等,來判斷該區域是不是滿足要求,如果滿足,則放到rectlist中。然后將該list中的所有點都置黑。轉到(1)履行。

(5)如果rectlist為空,則沒有獲得到目標rect。如果>=1 則將之依照1個規則進行排序(應當是那個最主要的特點),然后輸出最可能的那個rect。

算法進程演示以下:

原圖:

色采過濾(為了得到效果好1點的canny圖):

canny圖:

檢測畫框與擦除:

第1次 畫框:

第1次擦除:

第2次畫框:

第2次擦除

n次畫框:

n次擦除:

最后的甚么都沒剩下:

得出結果:

詳細算法代碼以下:

FindIdCode.h


#include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include < io.h> #include <algorithm> #include <stdio.h> #include "opencv/cv.h" #include "opencv/cxcore.h" #include "opencv2/highgui/highgui_c.h" #include "direct.h" using namespace cv; using namespace std; class CGetIDCOde { public: CGetIDCOde(); //刪除文件 并返回string 值 string getFilePath( const char * szBuf); //獲得文件長度 long GetFileLength(const char * filepath); //過濾色彩 void FilterColor(string strImgFileName); //找到目標連通域 RECT FindTargetConnectedDomain(); //將list中的點都設置成某1個色彩 void SetPointListColor(Mat & srcImg, std::vector<cv::Point> pointList, int nColor); //根據點列表獲得最小包括區域 void GetRectFromPointList(std::vector<cv::Point>& pointList, RECT & rtRect); //獲得與該點鄰近的點 void GetNearPoint(Mat & srcImg,cv::Point currentPoint, std::vector<cv::Point> & pointList); //將1個box框畫成某1個色彩 void DrowBoxColor(Mat &srcImg, std::vector<RECT> &boxList, int nColor); //獲得1個聯通區域 BOOL GetOneConnectedDomain(Mat & srcImg, std::vector<cv::Point>& pointList, RECT &rect); //將圖象的某1個區域保存為圖象 void SavePicWithDestRect(string strSource, string strDest, RECT destRect); //獲得身份證號圖象區域 RECT GetIdCode(const char * szSourceFile); //邊沿檢測 int outLinePic2(); char szCurrentPath[MAX_PATH]; string strOrigin; string strSave1; string strSave1_1; string strSave2; string strSave3; string strSave4; string strSave5; string strSave3_0; string strSave3_1; string strSave3_2; string strSave3_3; string strSave6; string strSave7; string strSave8; };

FindIdCode.cpp

#include "FindIdCode.h" int mMAX_DIS = 0; double fScale = 0.0; #define BOX_WIDTH 50 #define BLACK 0 #define MID_BLACK_WHITE 128 #define WHITE 255 #define RATE 0.2 //依照框的寬度排序 BOOL SortByM5(RECT &v1, RECT &v2) { int nWidth1 = v1.right - v1.left; int nHeight1 = v1.bottom - v1.top; int nWidth2 = v2.right - v2.left; int nHeight2 = v2.bottom - v2.top; float fRate1 = 1.0 * nWidth1 / nHeight1; float fRate2 = 1.0 * nWidth2 / nHeight2; if (fRate1 > fRate2) { return TRUE; } else { return FALSE; } } string CGetIDCOde::getFilePath( const char * szBuf) { string str; str = szCurrentPath; str += "\\"; str += szBuf; //刪除已存在的文件 DeleteFile(str.c_str()); return str; } long CGetIDCOde::GetFileLength(const char * filepath) { FILE* file = fopen(filepath, "rb"); if (file) { long size = filelength(fileno(file)); return size; } else { return 0; } } //色彩過濾 void CGetIDCOde::FilterColor(string strImgFileName) { uchar uDifferMax = 80; uchar rMax = 100; uchar bMax = 150; uchar gMax = 150; uchar uWhite = 255; uchar r,b,g; IplImage *workImg = cvLoadImage(strImgFileName.c_str(), CV_LOAD_IMAGE_UNCHANGED); //像素太高的進行縮放 if (workImg->width > 900) { int nTargetWidth = 600; fScale = 1.0 * workImg->width / nTargetWidth; CvSize czSize; //計算目標圖象大小 czSize.width = nTargetWidth; czSize.height = workImg->height / fScale; //IplImage *pSrcImage = cvLoadImage(strSave2.c_str(), CV_LOAD_IMAGE_UNCHANGED); IplImage *pDstImage = cvCreateImage(czSize, workImg->depth, workImg->nChannels); cvResize(workImg, pDstImage, CV_INTER_AREA); cvReleaseImage(&workImg); cvSaveImage(strSave1_1.c_str(),pDstImage); workImg = pDstImage; } for(int x=0;x<workImg->height;x++) { for(int y=0;y<workImg->width;y++) { b=((uchar*)(workImg->imageData+x*workImg->widthStep))[y*3+0]; g=((uchar*)(workImg->imageData+x*workImg->widthStep))[y*3+1]; r=((uchar*)(workImg->imageData+x*workImg->widthStep))[y*3+2]; //偏色比較嚴重的 //uchar uMax = max(max(b,g),r); //uchar uMin = min(min(b,g),r); //if ( uMax - uMin > uDifferMax) int nAbove = 0; if (b >= uDifferMax) { nAbove ++; } if (g >= uDifferMax) { nAbove ++; } if (r >= uDifferMax) { nAbove ++; } //有兩個大于80 if(nAbove >= 2 || b > bMax || g > gMax || r > rMax) { ((uchar*)(workImg->imageData+x*workImg->widthStep))[y*3+0] = uWhite; ((uchar*)(workImg->imageData+x*workImg->widthStep))[y*3+1] = uWhite; ((uchar*)(workImg->imageData+x*workImg->widthStep))[y*3+2] = uWhite; } } } cvSaveImage(strSave1.c_str(), workImg); } int CGetIDCOde::outLinePic2() { Mat src = imread(strSave1.c_str()); Mat dst; if (!src.empty()) { //輸入圖象 //輸出圖象 //輸入圖象色彩通道數 //x方向階數 //y方向階數 Sobel(src,dst,src.depth(),1,1); //imwrite("sobel.jpg",dst); //輸入圖象 //輸出圖象 //輸入圖象色彩通道數 Laplacian(src,dst,src.depth()); imwrite("laplacian.jpg",dst); //輸入圖象 //輸出圖象 //彩色轉灰度 cvtColor(src,src,CV_BGR2GRAY); //canny只處理灰度圖 //輸入圖象 //輸出圖象 //低閾值 //高閾值,opencv建議是低閾值的3倍 //內部sobel濾波器大小 //threshold1和threshold2 當中的小閾值用來控制邊沿連接,大的閾值用來控制強邊沿的初始分割。50 150 Canny(src,dst,220,240,3); imwrite(strSave2.c_str(),dst); return 0; } else { cout<< "IMG is not exist!"; return ⑴; } } void CGetIDCOde::SetPointListColor(Mat & srcImg, std::vector<cv::Point> pointList, int nColor) { for (int i = 0; i < pointList.size(); i ++) { int x = pointList[i].x; int y = pointList[i].y; *(srcImg.data + srcImg.step[0] * y + srcImg.step[1] * x) = nColor; } } RECT CGetIDCOde::FindTargetConnectedDomain() { Mat srcImg = imread(strSave2.c_str(), CV_LOAD_IMAGE_GRAYSCALE); //設定最大的距離 mMAX_DIS = srcImg.cols * (1.0 * 9 / 400) + 1; int nMaxWidth = 0.6 * srcImg.cols; int nMaxHeight = 1.0 * 5 * srcImg.rows / 36 ; std::vector<cv::Point> pointList; //探測1個矩形連通域,判斷是不是符合目標特點,不符合刪除找下1個。 //找到1個放入vector中。 std::vector<RECT> targetRectList; while(TRUE) { RECT rect; GetOneConnectedDomain(srcImg, pointList,rect); //判斷該rect是不是符合要求。 int nWidth = rect.right - rect.left; int nHeight = rect.bottom - rect.top; // 300 20 float fRate = 1.0 * nWidth / nHeight; if (nHeight > 5 && nHeight < nMaxHeight && nWidth > 100 && nWidth < nMaxWidth && fRate > 8 && fRate < 20) { //SavePicWithDestRect(strOrigin, strSave8, rect); targetRectList.push_back(rect); //break; } else { if (pointList.empty()) { break; } } //置黑然后找下1個 SetPointListColor(srcImg, pointList, BLACK); imwrite(strSave3_3.c_str(),srcImg); pointList.clear(); } //有多個排序 if (targetRectList.size() > 0) { sort(targetRectList.begin(), targetRectList.end(), SortByM5); //找到 提取圖象 保存。 RECT rect = targetRectList[0]; rect.left -= mMAX_DIS; if (rect.left < 0) { rect.left = 0; } rect.top -= mMAX_DIS; if (rect.top < 0) { rect.top = 0; } rect.right += mMAX_DIS; if (rect.right > srcImg.cols) { rect.right = srcImg.cols; } rect.bottom += mMAX_DIS; if (rect.bottom > srcImg.rows) { rect.bottom = srcImg.rows; } if (fScale > 0.0) { rect.left *= fScale; rect.right*= fScale; rect.bottom *= fScale; rect.top *= fScale; } return rect; //SavePicWithDestRect(strOrigin, strSave8, rect); } else { //cout<< "find no numbers!"; //getchar(); RECT rect; rect.bottom = rect.top = rect.left = rect.right = 0; return rect; } } //保存圖象 void CGetIDCOde::SavePicWithDestRect(string strSource, string strDest, RECT destRect) { IplImage* src; IplImage* dst; src = cvLoadImage(strSource.c_str(),1); if(!src) { return ; } cvSetImageROI(src,cvRect(destRect.left,destRect.top ,destRect.right - destRect.left, destRect.bottom - destRect.top)); dst = cvCreateImage(cvSize(destRect.right - destRect.left, destRect.bottom - destRect.top), IPL_DEPTH_8U, src->nChannels); cvCopy(src,dst,0); cvResetImageROI(src); cvSaveImage(strDest.c_str(), dst); cvReleaseImage(&dst); cvReleaseImage(&src); } BOOL CGetIDCOde::GetOneConnectedDomain(Mat & srcImg, std::vector<cv::Point>& pointList, RECT &rect) { int nWidth = srcImg.cols; int nHeight = srcImg.rows; int nXStart = 0; int nYStart = 0; BOOL bBlack = TRUE; BOOL bBreak = FALSE; int nWhite = 0; //找到第1個最上角的白點 for (int y = 0; y < nHeight; y ++) { for (int x = 0; x < nWidth; x++) { int nPixel = (int)(*(srcImg.data + srcImg.step[0] * y + srcImg.step[1] * x)); if (nPixel > MID_BLACK_WHITE) { nXStart = x; nYStart = y; cv::Point tempPint(nXStart,nYStart); pointList.push_back(tempPint); bBreak = TRUE; break; } } if (bBreak) { break; } } int nSize = pointList.size(); //探測下1個點。 for (int i = 0; i < nSize; i ++) { cv::Point currentPoint = pointList[i]; GetNearPoint(srcImg, currentPoint, pointList); nSize = pointList.size(); //如果超過4000個點則刪除后重新再來 if (nSize > 3000) { break; } } //對該pointList求最小包括的矩形框。 GetRectFromPointList(pointList, rect); std::vector<RECT> tempTect; tempTect.push_back(rect); DrowBoxColor(srcImg,tempTect, WHITE); imwrite(strSave3_2.c_str(),srcImg); DrowBoxColor(srcImg,tempTect, BLACK); return TRUE; } void CGetIDCOde::GetRectFromPointList(std::vector<cv::Point>& pointList, RECT & rtRect) { int nLeft = 0; int nTop = 0; int nRight = 0; int nBottom = 0; for(int i = 0; i < pointList.size(); i ++) { cv::Point tempPoint = pointList[i]; if (i == 0) { nLeft = nRight = tempPoint.x; nTop = nBottom = tempPoint.y; } else { if (tempPoint.x < nLeft) { nLeft = tempPoint.x; } if (tempPoint.x > nRight) { nRight = tempPoint.x; } if (tempPoint.y < nTop) { nTop = tempPoint.y; } if (tempPoint.y > nBottom) { nBottom = tempPoint.y; } } } rtRect.left = nLeft; rtRect.top = nTop; rtRect.right = nRight; rtRect.bottom = nBottom; } void CGetIDCOde::GetNearPoint(Mat & srcImg,cv::Point currentPoint, std::vector<cv::Point> & pointList) { //探測以該點為中心的 20 * 20范圍的點。 for (int y = max(0, currentPoint.y - mMAX_DIS); y < min(srcImg.rows, currentPoint.y + mMAX_DIS); y ++) { for (int x = max(currentPoint.x - mMAX_DIS, 0); x < min(srcImg.cols, currentPoint.x + mMAX_DIS); x ++) { int nPixel = (int)(*(srcImg.data + srcImg.step[0] * y + srcImg.step[1] * x)); if (nPixel > MID_BLACK_WHITE) { cv::Point tempPint(x, y); //看該點是不是已放入list std::vector<cv::Point>::iterator itFind = find( pointList.begin(), pointList.end(),tempPint); if (itFind == pointList.end()) { pointList.push_back(tempPint); } } } } } //畫框線為1個色彩 void CGetIDCOde::DrowBoxColor(Mat &srcImg, std::vector<RECT> &boxList, int nColor) { int nResultSize = boxList.size(); for (int i = 0; i < nResultSize; i ++) { RECT tempRect = boxList[i]; //上下邊線 int y1 = tempRect.top; int y2 = tempRect.bottom; for (int x = tempRect.left; x <= tempRect.right; x ++) { *(srcImg.data + srcImg.step[1] * x + srcImg.step[0] * y1) = nColor; *(srcImg.data + srcImg.step[1] * x + srcImg.step[0] * y2) = nColor; } //左右側線 int x1 = tempRect.left; int x2 = tempRect.right; for (int y = tempRect.top; y <= tempRect.bottom; y ++) { *(srcImg.data + srcImg.step[1] * x1 + srcImg.step[0] * y) = nColor; *(srcImg.data + srcImg.step[1] * x2 + srcImg.step[0] * y) = nColor; } } } RECT CGetIDCOde::GetIdCode(const char * szSourceFile) { CopyFile(szSourceFile, strOrigin.c_str(), FALSE); //文件大小 太小則不進行圖象過濾 RECT rect; rect.bottom = rect.top = rect.left = rect.right = 0; long nFileLen = GetFileLength(strOrigin.c_str()); if (nFileLen == 0) { return rect; } else if (nFileLen > 7000 ) { FilterColor(strOrigin); } else { CopyFile(strOrigin.c_str(), strSave1.c_str(),FALSE ); } if (outLinePic2() == ⑴) { return rect; } return FindTargetConnectedDomain(); } CGetIDCOde::CGetIDCOde() { _getcwd(szCurrentPath,MAX_PATH); strOrigin = getFilePath("imageText.jpg"); strSave1 = getFilePath("imageText_D.jpg"); strSave1_1 = getFilePath("imageText_ReSize.jpg"); strSave2 = getFilePath("canny.jpg"); strSave3 = getFilePath("imageText_Clear0.jpg"); strSave4 = getFilePath("imageText_Clear1.jpg"); strSave5 = getFilePath("imageText_Clear2.jpg"); strSave3_0 = getFilePath("imageText_Clear3_0.jpg"); strSave3_1 = getFilePath("imageText_Clear3_1.jpg"); strSave3_2 = getFilePath("imageText_Clear3_2.jpg"); strSave3_3 = getFilePath("imageText_Clear3_3.jpg"); strSave6 = getFilePath("imageText_Clear3.jpg"); strSave7 = getFilePath("imageText_D.jpg"); strSave8 = getFilePath("imageText_Clear4.jpg"); }

類的測試代碼:

#include "../FindIdCode/FindIdCode.h" using namespace std; #ifdef _DEBUG #pragma comment(lib, "Debug/FindIdCode.lib") #else #pragma comment(lib, "Release/FindIdCode.lib") #endif int main(int argc, char **argv) { if(argc < 2) return(1); CGetIDCOde getIdcode; //char* szSourceFile = "D:\\scan\\00000000000000000\\3032_024.jpg"; //dll測試 char* szSourceFile = argv[1]; RECT rect = getIdcode.GetIdCode(szSourceFile); //CopyFile(szSourceFile,strOrigin.c_str(), FALSE); getIdcode.SavePicWithDestRect(szSourceFile, getIdcode.strSave8, rect); cout<<"the rect is "<<rect.left<<" "<<rect.top<<" "<<rect.bottom<<" "<<rect.right<<" "; return 0; }


說明:

由于不斷的進行循環檢測,如果像素太高圖片太大則耗時較多,而且邊沿檢測效果特別不好,所以程序中對像素寬度大于900的則縮放到400。

程序運行效果的好壞直接影響因數是 canny圖片的效果。所以對不同特點的圖片,可以調劑canny函數的參數,如本例中采取的參數是:Canny(src,dst,220,240,3)。

色采過濾:由于身份證有很多藍色和紅色的底紋,將rgb過大的色采變成了白色。有時候其實不1定會有好的效果,反而會讓邊沿增多,反而影響結果。另外如果圖象特別模糊,最好也不要進行色采過濾。

最后還是需要提示1下opencv的環境問題。

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 请看一下欧美一级毛片 | 国产免费资源高清小视频在线观看 | 在线国产中文字幕 | 久草三级 | 2021天天躁夜夜躁狠狠躁 | 拔擦拔擦8x华人免费久久 | 欧美色老汉| 欧美日韩乱码毛片免费观看 | 精品国产第一页 | 日韩精品欧美精品中文精品 | 亚洲国产片高清在线观看 | 亚洲a色 | 欧美性生活视频免费播放网址大全观看 | 天天综合天天做天天综合 | 国产一区二区免费播放 | 国产亚洲精品自在线观看 | 亚洲另类图片小说 | 免费毛片二级c片观看动漫 免费毛片全部不收费的 | 欧美成人性色大片在线观看 | 最近新中文字幕大全高清视频 | www.99精品视频在线播放 | 亚洲主播在线 | 国产精品久久一区 | www.av视频在线观看 | 国产精品久久一区二区三区 | 国产v亚洲v天堂a无 国产v亚洲v天堂无码 | 日本不卡一区二区三区在线观看 | 亚洲欧美综合一区二区三区四区 | 精品久久久久久免费影院 | 久久成人视 | 欧美日韩一品道 | 欧美日韩一级黄色片 | 国产亚洲欧美日本一二三本道 | 国产69成人免费视频观看 | 久久综合欧美成人 | 亚洲第一成网站 | 中文字幕视频一区二区 | 欧美一级一毛片 | 秋霞一级成人欧美理论 | www.av片| 亚洲国产成人99精品激情在线 |