您好,登錄后才能下訂單哦!
這篇文章主要講解了openCV去除文字中亂入的線條的方法示例,內(nèi)容清晰明了,對(duì)此有興趣的小伙伴可以學(xué)習(xí)一下,相信大家閱讀完之后會(huì)有幫助。
今天上午,朋友發(fā)來(lái)一張圖片如下。沒(méi)錯(cuò),這就是原圖,他希望可以通過(guò)一些簡(jiǎn)單的算法將圖中這條穿過(guò)單詞間的直線去掉,使得到的結(jié)果能夠通過(guò)他的文字識(shí)別算法并得出正確結(jié)果——The Techniques of Machine Vision。
乍一看這似乎挺簡(jiǎn)單,(1)將圖像二值化;(2)找出這條直線;(3)將直線區(qū)域填成背景色(即白色);(4)再通過(guò)膨脹、腐蝕等操作將單詞缺失的部分給補(bǔ)全。以上4步似乎可以滿(mǎn)足要求,但測(cè)試發(fā)現(xiàn),效果不盡人意。
一、按上述方法實(shí)現(xiàn)過(guò)程
二值化結(jié)果如圖1.1所示,可以看到圖像并不標(biāo)準(zhǔn),直線粗細(xì)也不一,我們嘗試用霍夫變換找一下直線,代碼如下
void findLines(IplImage* raw, IplImage* dst) { IplImage* src = cvCloneImage(raw); IplImage* canny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); cvCanny(src, canny, 20, 200, 3); CvMemStorage* stor = cvCreateMemStorage(0); CvSeq* lines = NULL; lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30); cvZero(dst); CvPoint maxStart, maxEnd; int maxDistance = 0; for (int i = 0; i < lines->total; i++) { CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i); if (abs(line[0].x - line[1].x) > maxDistance) { maxDistance = abs(line[0].x - line[1].x); maxStart = line[0]; maxEnd = line[1]; } } cvLine(dst, maxStart, maxEnd, cvScalar(255), 1); cvReleaseImage(&src); cvReleaseMemStorage(&stor); }
簡(jiǎn)要解釋一下這段代碼。函數(shù)的功能是在輸入圖像中找出一條直線,輸入的圖像是灰度圖raw,返回值為dst,返回值是以圖片的形式,將找到的直線畫(huà)上圖中。
函數(shù)lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30);的參數(shù)表明,要求直線長(zhǎng)度在200個(gè)像素以上,且兩條在同一直線上的線段,如果相隔不到30個(gè)像素,就把它們連起來(lái)【注:圖片尺寸為1066×148】。對(duì)于找到的多條直線,認(rèn)為最長(zhǎng)的一條是我們要找的那條。找距離時(shí)用了abs(line[0].x - line[1].x);是不嚴(yán)格的,嚴(yán)格來(lái)講應(yīng)該是
sqrt((line[0].x - line[1].x)*(line[0].x - line[1].x)+(line[0].y - line[1].y)*(line[0].x - line[1].x))
不過(guò)圖中的直線接近水平,這里就簡(jiǎn)化一下啦。
所以將運(yùn)行這段代碼后,返回的圖片dst應(yīng)該是這樣子的
圖1.2中直線的粗線可以通過(guò)改變cvLine(dst, maxStart, maxEnd, cvScalar(255), 1);最后一個(gè)參數(shù)來(lái)調(diào)整,這里用的是1。
接下來(lái)步驟就是在二值化圖(圖1.1)中去掉這條線,代碼如下:
void eraseLine(IplImage* src, IplImage* flag) {// flag為圖1.2所示的圖片,src為圖1.1所示的二值化圖片 for (int row = 0; row < src->height; row++) for (int col = 0; col < src->width; col++) { // 如果在白色線段上,則將二值化圖片填為白色 if (cvGet2D(flag, row, col).val[0] == 255) cvSet2D(src, row, col, cvScalar(255)); } }
當(dāng)直線的寬度分別為2、3個(gè)像素時(shí),二值化圖去掉直線后的效果如下
可以看到,效果很差,如果要膨脹(黑色部分減?。瑔卧~下邊部分都會(huì)消失了,直接腐蝕(黑色部分增大),線又不能完全去掉。
后來(lái),我采用的辦法是,對(duì)圖1.3重新找一次直線(減去一次直線后,中間還殘留一部分短些的直線),再減掉,再找再減掉。后面再對(duì)圖像進(jìn)行腐蝕(黑色部分增長(zhǎng))。最終效果最好這就如下圖所示
但這種方法用時(shí)長(zhǎng)、針對(duì)不同的直線,找直線-減直線 的重復(fù)次數(shù)還不一樣,不具有可移植性。而且啊,這個(gè)圖片識(shí)別出來(lái)的結(jié)果是
The Technique_sJ_otMachine Vision
所以需要采用新的辦法來(lái)解決這個(gè)問(wèn)題。
二、新的辦法
源代碼如下
#include <cv.h> #include <highgui.h> #include <iostream> using namespace std; /* 函數(shù)功能:在輸入圖像中找一條直線 輸入輸出:輸入的圖像是灰度圖raw,返回值為dst,返回值是一條白色的線 lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30); 參數(shù)中的200是指要找的直線長(zhǎng)度要在200個(gè)像素以上; 參數(shù)中的30指的是兩條在同一直線上的線段,如果相隔不到30,則把它們連起來(lái) */ void findLines(IplImage* raw, IplImage* dst) { IplImage* src = cvCloneImage(raw); // clone the input image IplImage* canny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); // create a tmp image head to save gradient image cvCanny(src, canny, 20, 200, 3); // Generate its gradient image CvMemStorage* stor = cvCreateMemStorage(0); CvSeq* lines = NULL; // find a line whose length bigger than 200 pixels lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30); cvZero(dst); CvPoint maxStart, maxEnd; // save the coordinate of the head and rear of the line we want int maxDistance = 0; // The maximum distance of all lines found by [cvHoughLines2] for (int i = 0; i < lines->total; i++) // lines->total: the number of lines { // variable 'lines' is a sequence, [cvGetSeqElem] gets the (i)th line, and it returns its head and rear. CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i); // line[0] and line[1] is respectively the line's coordinate of its head and rear if (abs(line[0].x - line[1].x) > maxDistance) {/* It's a trick because the line is almost horizontal. strictly, it should be sqrt((line[0].x - line[1].x)*(line[0].x - line[1].x)+(line[0].y - line[1].y)*(line[0].x - line[1].x)) */ maxDistance = abs(line[0].x - line[1].x); maxStart = line[0]; maxEnd = line[1]; } } cvLine(dst, maxStart, maxEnd, cvScalar(255), 1); // draw the white line[cvScalar(255)] in a black background cvReleaseImage(&src); // free the memory cvReleaseMemStorage(&stor); } /* 函數(shù)功能:擦除面積小于【15個(gè)像素】的小塊兒 輸入輸出:無(wú)返回值,直接對(duì)輸入的圖像進(jìn)行操作 */ void erase(IplImage* raw) { IplImage* src = cvCloneImage(raw); /*Binarization and inverse the black and white because the function next only find white area while the word in image is black.*/ cvThreshold(src, src, 120, 255, CV_THRESH_BINARY_INV); // create some space to save the white areas but we access it via variable 'cont' CvMemStorage* stor = cvCreateMemStorage(0); CvSeq* cont; cvFindContours(src, stor, &cont, sizeof(CvContour), CV_RETR_EXTERNAL); // find the white regions for (; cont; cont = cont->h_next) // Traversal { if (fabs(cvContourArea(cont)) < 15) // if its Area smaller than 15, we fill it with white[cvScalar(255)] cvDrawContours(raw, cont, cvScalar(255), cvScalar(255), 0, CV_FILLED, 8); } cvReleaseImage(&src); } int main() { IplImage* src = cvLoadImage("D:/test.png"); cvNamedWindow("原圖", 1); cvShowImage("原圖", src); IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); IplImage* canny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); IplImage* dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); IplImage* binary = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); cvCvtColor(src, gray, CV_RGB2GRAY); cvThreshold(gray, binary, 120, 255, CV_THRESH_OTSU); findLines(gray, dst); cvNamedWindow("dst", 1); cvShowImage("dst", dst); for (int row = 0; row < binary->height; row++) for (int col = 0; col < binary->width; col++) { if (cvGet2D(dst, row, col).val[0] == 255) { int up = 0, down = 0; int white = 0; for (int i = row; i >= 0; i--) { if (cvGet2D(binary, i, col).val[0] == 0) { up++; white = 0; } else white++; if(white > 2) break; } white = 0; for (int i = row; i < binary->height; i++) { if (cvGet2D(binary, i, col).val[0] == 0) { down++; white = 0; } else white++; if (white > 2) break; } if (up + down < 8) { for (int i = -up; i <= down; i++) cvSet2D(binary, row + i, col, cvScalar(255)); } } } cvNamedWindow("結(jié)果", 1); cvShowImage("結(jié)果", binary); erase(binary); //cvDilate(binary, binary, NULL, 1); cvErode(binary, binary, NULL, 1); cvNamedWindow("膨脹腐蝕", 1); cvShowImage("膨脹腐蝕", binary); cvSaveImage("D:/result.png", binary); cvReleaseImage(&src); cvReleaseImage(&canny); cvReleaseImage(&gray); cvReleaseImage(&dst); cvReleaseImage(&binary); cvWaitKey(0); return 0; }
這個(gè)方法很簡(jiǎn)單的,就是在找到直線(直線寬度為1)后,沿著直線從左到右對(duì)二值化圖進(jìn)行上下掃描,如果這個(gè)直線的寬度(黑色的寬度)小于8個(gè)像素,則認(rèn)為它只是直線,而不是文字的一部分,那么將它填成白色;反之,對(duì)于直線是文字的一部分這種情況,則不對(duì)它進(jìn)行任何操作。
這樣得到的結(jié)果如下圖2.1所示
當(dāng)然這個(gè)結(jié)果有點(diǎn)差強(qiáng)人意,如果你有更好的想法,請(qǐng)?jiān)谙旅媪粞?,我們交流交流?/p>
看完上述內(nèi)容,是不是對(duì)openCV去除文字中亂入的線條的方法示例有進(jìn)一步的了解,如果還想學(xué)習(xí)更多內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。