国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

iOS利用OpenCV 實現文字行區域提取的嘗試

番茄西紅柿 / 3135人閱讀

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

一些探索

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

額,好吧,那我就研究下他是如何把選中的文字和百度OCR的結果進行對應的,等等,讓我先抓個包看看。

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

著手實現

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

根據OpenCV入門筆記(七) 文字區域的提取中提供的思路,我實現了OC版本的代碼,通過測試,清晰的文字截圖識別沒有問題,但是在復雜的拍照場景中幾乎無法識別任何內容,例如下圖

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

    將圖片轉為灰度圖

    形態學變換的預處理,得到可以查找矩形的圖片

    查找和篩選文字區域

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

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

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

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

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

既然如此,就需要我們修改一下在第二步的處理流程了,在反轉圖像(由黑白變為白黑)之前,需要對圖像進行降噪處理,因為OpenCV是對亮點進行操作,在黑白圖像中降噪更容易處理(去除雜亂黑點),降噪使用的方法仍然是上面的膨脹和腐蝕法

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

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

//膨脹一次
cv::Mat dilateelement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4,2));
cv::Mat dilate1;
dilate(binary, dilate1, dilateelement);
    
//輕度腐蝕一次,去除噪點
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);

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

看一下經過兩次降噪之后的圖像是怎么樣的

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

做完二值化反轉之后是上面這個樣子的,接下來再對圖片做膨脹->腐蝕->膨脹處理

//二值化 第二次二值化將黑白圖像反轉 文字變亮
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);

//腐蝕一次,去掉細節,表格線等。這里去掉的是豎直的線
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);

處理的結果圖如下:

最終的框選效果

當然調試過程中不止用了這一張圖片,畢竟結果要有一定的普適性,下面是其他幾種情況下的識別結果

好了,下面貼一下整個過程的源碼

+ (UIImage *)detect:(UIImage *) image {
    
    cv::Mat img;
    img = [self cvMatFromUIImage:image];
    
    //1.轉化成灰度圖
    cv::Mat gray;
    cvtColor(bigImg, gray, cv::COLOR_BGR2GRAY);
    
    //2.形態學變換的預處理,得到可以查找矩形的輪廓
    cv::Mat dilation = [self preprocess:gray];
    
    //3.查找和篩選文字區域
    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 {
    
    //第一次二值化,轉為黑白圖片
    cv::Mat binary; 				  
    cv::adaptiveThreshold(gray, binary, 255,cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 31, 10);
    
    //在第二次二值化之前 為了去除噪點 做了兩次膨脹腐蝕,OpenCV是對亮點進行操作,在黑白圖像中降噪更容易處理(去除雜亂黑點)
    
    //膨脹一次
    cv::Mat dilateelement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4,2));
    cv::Mat dilate1;
    dilate(binary, dilate1, dilateelement);
    
    //輕度腐蝕一次,去除噪點
    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);
    
    //輕度腐蝕一次,去除噪點
    cv::Mat element12 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5,1));
    cv::Mat erode12;
    erode(dilate12, erode12, element12);
    
    //////////////////////////////////////////////////////////
    //二值化 第二次二值化將黑白圖像反轉 文字變亮
    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);

    //腐蝕一次,去掉細節,表格線等。這里去掉的是豎直的線
    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++) {
        //計算當前輪廓的面積
        double area = cv::contourArea(contours[i]);
        //面積小于1000的全部篩選掉
        if (area < 1000)
            continue;
        //輪廓近似,作用較小,approxPolyDP函數有待研究
        double epsilon = 0.001*arcLength(contours[i], true);
        cv::Mat approx;
        approxPolyDP(contours[i], approx, epsilon, true);
        
        //找到最小矩形,該矩形可能有方向
        cv::RotatedRect rect = minAreaRect(contours[i]);
        
        //計算高和寬
        int m_width = rect.boundingRect().width;
        int m_height = rect.boundingRect().height;
        
        //篩選那些太細的矩形,留下扁的
        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;
}

這里還有幾個cv::Mat 與 UIImage相互轉換的方法一并提供

//從UIImage對象轉換為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轉換單通道的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轉換為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;
}

結語

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

其他參考內容

OpenCV處理拍照表格(一)

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7072.html

相關文章

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

    摘要:的文本檢測器是一種基于新穎架構和訓練模式的深度學習模型。深度學習文本檢測器圖文本檢測全卷積網絡的結構等人的圖。隨著和的發布,我們現在可以使用一種名為的基于深度學習的文本檢測器,它基于等人的年論文一種高效精確的場景文本檢測器。 by Adrian Rosebrock on August 20, 2018 in Deep Learning, Optical Character Recogn...

    VincentFF 評論0 收藏0
  • [譯]OpenCV OCR and text recognition with Tesseract

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

    gnehc 評論0 收藏0
  • 10Python實現更快更準人臉識別

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

    2i18ns 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<