成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

iOS利用OpenCV 實(shí)現(xiàn)文字行區(qū)域提取的嘗試

番茄西紅柿 / 3175人閱讀

摘要:這是坐標(biāo)百度,好像沒啥好研究的了,不過出于好奇還是想知道使用是如何做到把文字區(qū)域進(jìn)行框選的,所以接下來我們就看看如何在上使用實(shí)現(xiàn)圖片中的文字框選。

一些探索

最近下了幾個(gè)OCR的App(比如白描),發(fā)現(xiàn)可以選中圖片中的文字行逐行轉(zhuǎn)成文字,覺得很有意思(當(dāng)然想用要花錢啦),想著自己研究一下實(shí)現(xiàn)原理,google之后,發(fā)現(xiàn)了兩個(gè)庫(kù),一個(gè)是OpenCV,在機(jī)器視覺方面應(yīng)用廣泛,圖像分析必備利器。另一個(gè)是Tesseract,谷歌開源的文字識(shí)別框架,iOS端gali8編譯了一個(gè)Tesseract-OCR-iOS的庫(kù)可以使用,但是集成過程不是很愉快,Tesseract-OCR-iOS使用的Tesseract 3.3版本,而Tesseract已經(jīng)更新到4.0,所以字庫(kù)不匹配問題搞的很煩,而且利用官方提供的訓(xùn)練字庫(kù)識(shí)別效果很差,想要實(shí)現(xiàn)高準(zhǔn)確率的識(shí)別效果需要自行進(jìn)行字庫(kù)訓(xùn)練,相當(dāng)繁瑣,并且工作量巨大,在完成demo之后就放棄使用了。接著,我又Google了一番,得到的答案是ABBYY是業(yè)界中文OCR識(shí)別效果最好的,其次是百度,于是我又點(diǎn)開了白描,在關(guān)于頁(yè)里看到了這個(gè)

額,好吧,那我就研究下他是如何把選中的文字和百度OCR的結(jié)果進(jìn)行對(duì)應(yīng)的,等等,讓我先抓個(gè)包看看。

這是…坐標(biāo)?百度666,好像沒啥好研究的了,不過出于好奇還是想知道使用openCV是如何做到把文字區(qū)域進(jìn)行框選的,所以接下來我們就看看如何在iOS上使用OpenCV實(shí)現(xiàn)圖片中的文字框選。

著手實(shí)現(xiàn)

首先,需要去OpenCV官網(wǎng)下載iOS的framework,下載好后拖入新建的工程中即可,由于OpenCV庫(kù)是使用C++編寫,所以swift無(wú)法直接使用,需要使用OC做橋接,需要使用swift的同學(xué)可以看下這篇文章Using OpenCV in an iOS app。

根據(jù)OpenCV入門筆記(七) 文字區(qū)域的提取中提供的思路,我實(shí)現(xiàn)了OC版本的代碼,通過測(cè)試,清晰的文字截圖識(shí)別沒有問題,但是在復(fù)雜的拍照?qǐng)鼍爸袔缀鯚o(wú)法識(shí)別任何內(nèi)容,例如下圖

這張是相機(jī)拍攝的屏幕上的文字,有清晰的豎紋及屏幕反光,在該算法下,最終的框選區(qū)域是整個(gè)圖片,無(wú)法識(shí)別文字區(qū)域,說明這個(gè)處理流程還是不完善的,我們先來看一下他的處理過程

    將圖片轉(zhuǎn)為灰度圖

    形態(tài)學(xué)變換的預(yù)處理,得到可以查找矩形的圖片

    查找和篩選文字區(qū)域

    用綠線畫出這些找到的輪廓

根據(jù)前面得到的識(shí)別結(jié)果,我們大致可以猜測(cè)問題出在了第二步,由于豎紋影響將全部文字區(qū)域連城一片,導(dǎo)致整圖被框選。那么在第二步中都做了哪些操作呢?

實(shí)際上上面的流程一共做了4步操作,二值化->膨脹->腐蝕->再膨脹,這個(gè)流程對(duì)于正常的白底文本截圖的識(shí)別沒有問題,一但圖片中出現(xiàn)了噪點(diǎn),噪點(diǎn)在第一次膨脹的之后被放大,對(duì)整個(gè)圖像產(chǎn)生不可逆的污染,我們先來看一下二值化后的圖像

文字還是很清晰的,但是豎紋一樣明顯,接著第二步膨脹,看下會(huì)怎樣

一片白,不用往下看了吧。

既然如此,就需要我們修改一下在第二步的處理流程了,在反轉(zhuǎn)圖像(由黑白變?yōu)榘缀冢┲?/strong>,需要對(duì)圖像進(jìn)行降噪處理,因?yàn)镺penCV是對(duì)亮點(diǎn)進(jìn)行操作,在黑白圖像中降噪更容易處理(去除雜亂黑點(diǎn)),降噪使用的方法仍然是上面的膨脹和腐蝕法

//第一次二值化,轉(zhuǎn)為黑白圖片
cv::Mat binary;  			    
cv::adaptiveThreshold(gray,binary,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY,31,10);

//在第二次二值化之前 為了去除噪點(diǎn) 做了兩次膨脹腐蝕

//膨脹一次
cv::Mat dilateelement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4,2));
cv::Mat dilate1;
dilate(binary, dilate1, dilateelement);
    
//輕度腐蝕一次,去除噪點(diǎn)
cv::Mat element3 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4,4));
cv::Mat erode11;
erode(dilate1, erode11, element3);
    
//第二次膨脹
cv::Mat dilateelement12 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
cv::Mat dilate12;
dilate(erode11, dilate12, dilateelement12);

//輕度腐蝕一次,去除噪點(diǎn)
cv::Mat element12 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
cv::Mat erode12;
erode(dilate12, erode12, element12);

看一下經(jīng)過兩次降噪之后的圖像是怎么樣的

豎紋基本上不見了,仍然還有一部分黑點(diǎn),但是已經(jīng)不影響后面的識(shí)別了,這里降噪只能適度,過度處理可能會(huì)使文字部分丟失。

做完二值化反轉(zhuǎn)之后是上面這個(gè)樣子的,接下來再對(duì)圖片做膨脹->腐蝕->膨脹處理

//二值化 第二次二值化將黑白圖像反轉(zhuǎn) 文字變亮
cv::Mat binary2;  
cv::adaptiveThreshold(erode12,binary2,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY_INV,17,10);

//橫向膨脹拉伸 文字連片形成亮條
cv::Mat dilateelement21 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(60,1));
cv::Mat dilate21;
dilate(binary2, dilate21, dilateelement21);

//腐蝕一次,去掉細(xì)節(jié),表格線等。這里去掉的是豎直的線
cv::Mat erodeelement21 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(30,1));
cv::Mat erode21;
erode(dilate21, erode21, erodeelement21);

//再次膨脹,讓輪廓明顯一些
cv::Mat dilateelement22 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
cv::Mat dilate22;
dilate(erode21, dilate22, dilateelement22);

處理的結(jié)果圖如下:

最終的框選效果

當(dāng)然調(diào)試過程中不止用了這一張圖片,畢竟結(jié)果要有一定的普適性,下面是其他幾種情況下的識(shí)別結(jié)果

好了,下面貼一下整個(gè)過程的源碼

+ (UIImage *)detect:(UIImage *) image {
    
    cv::Mat img;
    img = [self cvMatFromUIImage:image];
    
    //1.轉(zhuǎn)化成灰度圖
    cv::Mat gray;
    cvtColor(bigImg, gray, cv::COLOR_BGR2GRAY);
    
    //2.形態(tài)學(xué)變換的預(yù)處理,得到可以查找矩形的輪廓
    cv::Mat dilation = [self preprocess:gray];
    
    //3.查找和篩選文字區(qū)域
    std::vector rects = [self findTextRegion:dilation];
    
    //4.用線畫出這些找到的輪廓
    for (int i = 0; i < rects.size(); i++) {
        cv::Point2f P[4];
        cv::RotatedRect rect = rects[i];
        rect.points(P);
        for (int j = 0; j <= 3; j++) {
            cv::line(bigImg, P[j], P[(j + 1) % 4], cv::Scalar(0,0,255),2);
        }
    }
    
    return [self UIImageFromCVMat:bigImg];
}

+ (cv::Mat) preprocess:(cv::Mat)gray {
    
    //第一次二值化,轉(zhuǎn)為黑白圖片
    cv::Mat binary; 				  
    cv::adaptiveThreshold(gray, binary, 255,cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 31, 10);
    
    //在第二次二值化之前 為了去除噪點(diǎn) 做了兩次膨脹腐蝕,OpenCV是對(duì)亮點(diǎn)進(jìn)行操作,在黑白圖像中降噪更容易處理(去除雜亂黑點(diǎn))
    
    //膨脹一次
    cv::Mat dilateelement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4,2));
    cv::Mat dilate1;
    dilate(binary, dilate1, dilateelement);
    
    //輕度腐蝕一次,去除噪點(diǎn)
    cv::Mat element3 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4,4));
    cv::Mat erode11;
    erode(dilate1, erode11, element3);
    
    //第二次膨脹
    cv::Mat dilateelement12 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
    cv::Mat dilate12;
    dilate(erode11, dilate12, dilateelement12);
    
    //輕度腐蝕一次,去除噪點(diǎn)
    cv::Mat element12 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
    cv::Mat erode12;
    erode(dilate12, erode12, element12);
    
    //////////////////////////////////////////////////////////
    //二值化 第二次二值化將黑白圖像反轉(zhuǎn) 文字變亮
    cv::Mat binary2;
    cv::adaptiveThreshold(erode12, binary2, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, 17, 10);
    
    //橫向膨脹拉伸 文字連片形成亮條
    cv::Mat dilateelement21 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(60,1));
    cv::Mat dilate21;
    dilate(binary2, dilate21, dilateelement21);

    //腐蝕一次,去掉細(xì)節(jié),表格線等。這里去掉的是豎直的線
    cv::Mat erodeelement21 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(30,1));
    cv::Mat erode21;
    erode(dilate21, erode21, erodeelement21);

    //再次膨脹,讓輪廓明顯一些
    cv::Mat dilateelement22 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
    cv::Mat dilate22;
    dilate(erode21, dilate22, dilateelement22);

    return dilate22;
}

+ (std::vector) findTextRegion:(cv::Mat) img {
    
    std::vector rects;
    std::vector heights;
    //1.查找輪廓
    std::vector > contours;
    std::vector hierarchy;
    cv::Mat m = img.clone();
    cv::findContours(img,contours,hierarchy,
                     cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE,cv::Point(0,0));
    //2.篩選那些面積小的
    for (int i = 0; i < contours.size(); i++) {
        //計(jì)算當(dāng)前輪廓的面積
        double area = cv::contourArea(contours[i]);
        //面積小于1000的全部篩選掉
        if (area < 1000)
            continue;
        //輪廓近似,作用較小,approxPolyDP函數(shù)有待研究
        double epsilon = 0.001*arcLength(contours[i], true);
        cv::Mat approx;
        approxPolyDP(contours[i], approx, epsilon, true);
        
        //找到最小矩形,該矩形可能有方向
        cv::RotatedRect rect = minAreaRect(contours[i]);
        
        //計(jì)算高和寬
        int m_width = rect.boundingRect().width;
        int m_height = rect.boundingRect().height;
        
        //篩選那些太細(xì)的矩形,留下扁的
        if (m_height > m_width * 1.2)
            continue;
        //過濾很扁的
        if (m_height < 20)
            continue;
        heights.push_back(m_height);
        //符合條件的rect添加到rects集合中
        rects.push_back(rect);
    }
    
    return rects;
}

這里還有幾個(gè)cv::Mat 與 UIImage相互轉(zhuǎn)換的方法一并提供

//從UIImage對(duì)象轉(zhuǎn)換為4通道的Mat,即是原圖的Mat
+ (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;
    
    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels (color channels + alpha)
    
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,
                                                    cols,
                                                    rows,
                                                    8,
                                                    cvMat.step[0],
                                                    colorSpace,
                                                    kCGImageAlphaNoneSkipLast |
                                                    kCGBitmapByteOrderDefault);
    
    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
    CGContextRelease(contextRef);
    
    return cvMat;
}

//從UIImage轉(zhuǎn)換單通道的Mat,即灰度值
+ (cv::Mat)cvMatGrayFromUIImage:(UIImage *)image
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;
    
    cv::Mat cvMat(rows, cols, CV_8UC1); // 8 bits per component, 1 channels
    
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,
                                                    cols,
                                                    rows,
                                                    8,
                                                    cvMat.step[0],
                                                    colorSpace,
                                                    kCGImageAlphaNoneSkipLast |
                                                    kCGBitmapByteOrderDefault);
    
    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
    CGContextRelease(contextRef);
    
    return cvMat;
}

//將Mat轉(zhuǎn)換為UIImage
+ (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
    CGColorSpaceRef colorSpace;
    
    if (cvMat.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }
    
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    
    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(cvMat.cols,
                                        cvMat.rows,
                                        8,
                                        8 * cvMat.elemSize(),
                                        cvMat.step[0],                            
                                        colorSpace,
                                        kCGImageAlphaNone|kCGBitmapByteOrderDefault,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault
                                        );
    
    
    // Getting UIImage from CGImage
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
    
    return finalImage;
}

結(jié)語(yǔ)

調(diào)試是一個(gè)反復(fù)修改流程、修改參數(shù)的過程,至于為什么是這樣的流程和參數(shù)都是不斷嘗試之后,通過主觀感受得到的結(jié)果,有興趣的小伙伴可以自己修改下參數(shù)看看效果,如果有更好的方案歡迎你來和我交流探討,還有,如果真的要運(yùn)用到項(xiàng)目中,這個(gè)方案還是不完善的,比如黑底白字就沒辦法識(shí)別,所以還需要加入邏輯判斷,進(jìn)行不同的處理,我這里只是提供一個(gè)思路。最后附上demo地址,由于openCV框架很大,需要自行下載加入工程,pod文件也沒有上傳,請(qǐng)自行pod install,最后...歡迎Star。

其他參考內(nèi)容

OpenCV處理拍照表格(一)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7072.html

相關(guān)文章

  • [譯]OpenCV Text Detection (EAST text detector)

    摘要:的文本檢測(cè)器是一種基于新穎架構(gòu)和訓(xùn)練模式的深度學(xué)習(xí)模型。深度學(xué)習(xí)文本檢測(cè)器圖文本檢測(cè)全卷積網(wǎng)絡(luò)的結(jié)構(gòu)等人的圖。隨著和的發(fā)布,我們現(xiàn)在可以使用一種名為的基于深度學(xué)習(xí)的文本檢測(cè)器,它基于等人的年論文一種高效精確的場(chǎng)景文本檢測(cè)器。 by Adrian Rosebrock on August 20, 2018 in Deep Learning, Optical Character Recogn...

    VincentFF 評(píng)論0 收藏0
  • [譯]OpenCV OCR and text recognition with Tesseract

    摘要:納入深度學(xué)習(xí)模型來進(jìn)一步提升準(zhǔn)確率只是時(shí)間問題,事實(shí)上,這個(gè)時(shí)間已經(jīng)到來。最新版本支持基于深度學(xué)習(xí)的,準(zhǔn)確率顯著提高。該函數(shù)使用基于深度學(xué)習(xí)的文本檢測(cè)器來檢測(cè)不是識(shí)別圖像中的文本區(qū)域。高效使用概率最高的文本區(qū)域,刪除其他重疊區(qū)域。 By Adrian Rosebrock on September 17, 2018 in Deep Learning, Optical Character ...

    gnehc 評(píng)論0 收藏0
  • 10Python實(shí)現(xiàn)更快更準(zhǔn)人臉識(shí)別

    摘要:行代碼的人臉識(shí)別看了行的人臉識(shí)別一文后,簡(jiǎn)單嘗試了一下,發(fā)現(xiàn)識(shí)別準(zhǔn)確度不夠。膜拜完大神,直接開干首先,安裝以及相關(guān)依賴工具代碼略作改動(dòng)執(zhí)行之后效果是這樣的完美識(shí)別結(jié)論如果要做人臉識(shí)別的話,建議選擇,而不要選擇。 7行代碼(OpenCV)的人臉識(shí)別 看了《7行Python的人臉識(shí)別》一文后,簡(jiǎn)單嘗試了一下,發(fā)現(xiàn)識(shí)別準(zhǔn)確度不夠。原始圖像如下: showImg(https://segment...

    2i18ns 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<