テキストOpenCVの抽出


148

画像内のテキストの境界ボックスを見つけようとしていますが、現在このアプローチを使用しています。

// calculate the local variances of the grayscale image
Mat t_mean, t_mean_2;
Mat grayF;
outImg_gray.convertTo(grayF, CV_32F);
int winSize = 35;
blur(grayF, t_mean, cv::Size(winSize,winSize));
blur(grayF.mul(grayF), t_mean_2, cv::Size(winSize,winSize));
Mat varMat = t_mean_2 - t_mean.mul(t_mean);
varMat.convertTo(varMat, CV_8U);

// threshold the high variance regions
Mat varMatRegions = varMat > 100;

このような画像が与えられると:

ここに画像の説明を入力してください

それから私が示すとき私はvarMatRegionsこの画像を取得します:

ここに画像の説明を入力してください

ご覧のように、テキストの左側のブロックとカードのヘッダーが少し組み合わされています。ほとんどのカードでは、この方法はうまく機能しますが、使用頻度の高いカードでは問題が発生する可能性があります。

これらの輪郭が接続されないのは、輪郭のバウンディングボックスがカード全体をほぼ占めてしまうためです。

テキストを適切に検出するために、テキストを検索する別の方法を誰かが提案できますか?

これらの2つの上のカード内のテキストを見つけることができる人は誰でも200ポイントです。

ここに画像の説明を入力してください ここに画像の説明を入力してください


1
ここで私が見る最も簡単な方法は、領域を取得する前にコントラストを上げることです。
PawełStawarz

3
クールな質問。それを投稿し、そのような興味深い返信を確実にする賞金を主催してくれてありがとう。
Geoff、2014年

プログラミングの新機能。サンスクリット語のように英語以外のスクリプトのテキストにも同じことができますか?
Vamshi Krishna

回答:


127

(LPDからヒントを得た)近いエッジ要素を見つけることでテキストを検出できます。

#include "opencv2/opencv.hpp"

std::vector<cv::Rect> detectLetters(cv::Mat img)
{
    std::vector<cv::Rect> boundRect;
    cv::Mat img_gray, img_sobel, img_threshold, element;
    cvtColor(img, img_gray, CV_BGR2GRAY);
    cv::Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
    cv::threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
    element = getStructuringElement(cv::MORPH_RECT, cv::Size(17, 3) );
    cv::morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element); //Does the trick
    std::vector< std::vector< cv::Point> > contours;
    cv::findContours(img_threshold, contours, 0, 1); 
    std::vector<std::vector<cv::Point> > contours_poly( contours.size() );
    for( int i = 0; i < contours.size(); i++ )
        if (contours[i].size()>100)
        { 
            cv::approxPolyDP( cv::Mat(contours[i]), contours_poly[i], 3, true );
            cv::Rect appRect( boundingRect( cv::Mat(contours_poly[i]) ));
            if (appRect.width>appRect.height) 
                boundRect.push_back(appRect);
        }
    return boundRect;
}

使用法:

int main(int argc,char** argv)
{
    //Read
    cv::Mat img1=cv::imread("side_1.jpg");
    cv::Mat img2=cv::imread("side_2.jpg");
    //Detect
    std::vector<cv::Rect> letterBBoxes1=detectLetters(img1);
    std::vector<cv::Rect> letterBBoxes2=detectLetters(img2);
    //Display
    for(int i=0; i< letterBBoxes1.size(); i++)
        cv::rectangle(img1,letterBBoxes1[i],cv::Scalar(0,255,0),3,8,0);
    cv::imwrite( "imgOut1.jpg", img1);  
    for(int i=0; i< letterBBoxes2.size(); i++)
        cv::rectangle(img2,letterBBoxes2[i],cv::Scalar(0,255,0),3,8,0);
    cv::imwrite( "imgOut2.jpg", img2);  
    return 0;
}

結果:

a。element = getStructuringElement(cv :: MORPH_RECT、cv :: Size(17、3)); imgOut1 imgOut2

b。element = getStructuringElement(cv :: MORPH_RECT、cv :: Size(30、30)); imgOut1 imgOut2

上記の他の画像の結果も同様です。


6
ナンバープレート検出器。
LovaBill、2014年

2
一部のカードでは、文字の半分が切り取られるなど、境界ボックスがすべてのテキストを囲んでいない場合があります。このカードなど:i.imgur.com/tX3XrwH.jpg すべての境界バウンディングボックスの高さと幅を拡張するにはどうすればよいnですか?それが素晴らしい働きをするソリューションをありがとう!
クリップ

4
言ってくださいcv::Rect a;。nによって拡大:a.x-=n/2;a.y-=n/2;a.width+=n;a.height+=n;
LovaBill、2014年

2
こんにちは、python cv2で同じ結果を得るにはどうすればよいですか?
dnth、2015年


128

以下のプログラムでは、勾配に基づく方法を使用しました。結果の画像を追加しました。処理のために画像の縮小版を使用していることに注意してください。

c ++バージョン

The MIT License (MIT)

Copyright (c) 2014 Dhanushka Dangampola

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

#include "stdafx.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define INPUT_FILE              "1.jpg"
#define OUTPUT_FOLDER_PATH      string("")

int _tmain(int argc, _TCHAR* argv[])
{
    Mat large = imread(INPUT_FILE);
    Mat rgb;
    // downsample and use it for processing
    pyrDown(large, rgb);
    Mat small;
    cvtColor(rgb, small, CV_BGR2GRAY);
    // morphological gradient
    Mat grad;
    Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
    morphologyEx(small, grad, MORPH_GRADIENT, morphKernel);
    // binarize
    Mat bw;
    threshold(grad, bw, 0.0, 255.0, THRESH_BINARY | THRESH_OTSU);
    // connect horizontally oriented regions
    Mat connected;
    morphKernel = getStructuringElement(MORPH_RECT, Size(9, 1));
    morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);
    // find contours
    Mat mask = Mat::zeros(bw.size(), CV_8UC1);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
    // filter contours
    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
    {
        Rect rect = boundingRect(contours[idx]);
        Mat maskROI(mask, rect);
        maskROI = Scalar(0, 0, 0);
        // fill the contour
        drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
        // ratio of non-zero pixels in the filled region
        double r = (double)countNonZero(maskROI)/(rect.width*rect.height);

        if (r > .45 /* assume at least 45% of the area is filled if it contains text */
            && 
            (rect.height > 8 && rect.width > 8) /* constraints on region size */
            /* these two conditions alone are not very robust. better to use something 
            like the number of significant peaks in a horizontal projection as a third condition */
            )
        {
            rectangle(rgb, rect, Scalar(0, 255, 0), 2);
        }
    }
    imwrite(OUTPUT_FOLDER_PATH + string("rgb.jpg"), rgb);

    return 0;
}

pythonバージョン

The MIT License (MIT)

Copyright (c) 2017 Dhanushka Dangampola

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

import cv2
import numpy as np

large = cv2.imread('1.jpg')
rgb = cv2.pyrDown(large)
small = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
grad = cv2.morphologyEx(small, cv2.MORPH_GRADIENT, kernel)

_, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel)
# using RETR_EXTERNAL instead of RETR_CCOMP
contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
#For opencv 3+ comment the previous line and uncomment the following line
#_, contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

mask = np.zeros(bw.shape, dtype=np.uint8)

for idx in range(len(contours)):
    x, y, w, h = cv2.boundingRect(contours[idx])
    mask[y:y+h, x:x+w] = 0
    cv2.drawContours(mask, contours, idx, (255, 255, 255), -1)
    r = float(cv2.countNonZero(mask[y:y+h, x:x+w])) / (w * h)

    if r > 0.45 and w > 8 and h > 8:
        cv2.rectangle(rgb, (x, y), (x+w-1, y+h-1), (0, 255, 0), 2)

cv2.imshow('rects', rgb)

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください


3
私は彼のアプローチを見ました。主な違いは、彼がSobelフィルターを使用しているのに対し、形態勾配フィルターを使用していることです。形態学的フィルターとダウンサンプリングは、それほど強くないエッジの多くを平坦化すると思います。ソーベルは、より多くのノイズを拾う可能性があります。
dhanushka 2014年

1
@ascenator OTSUとしきい値タイプを組み合わせると、指定されたしきい値ではなく、大津のしきい値が使用されます。こちらをご覧ください
dhanushka

1
@VishnuJayanandにスケーリングを適用するだけrectです。1ありますpyrdownので、乗算、x, y, width, heightrect4で
dhanushka

1
第3の条件を教えていただけませんか:水平投影の有意なピークの数または少なくともいくつかのリード。
ISlimani 2018年

2
@DforTye塗りつぶされた輪郭の水平投影(cv :: reduce)を取り、それから(たとえば、平均または中央値の高さを使用して)しきい値を設定します。この結果を視覚化すると、バーコードのように見えます。当時は、小節の数を数え、しきい値を課すことを考えていたと思います。リージョンが十分にクリーンである場合は、OCRにフィードして、検出された各文字の信頼レベルを取得して、リージョンにテキストが含まれていることを確認することも役立つと思います。
dhanushka

51

これは、テキストブロックを検出するために使用した代替アプローチです。

  1. 画像をグレースケールに変換しました
  2. 適用されたしきい値(単純なバイナリしきい値、しきい値として150の厳選された値)
  3. 拡張を適用して画像の線を太くし、オブジェクトをよりコンパクトにし、余白の断片を減らします。反復回数に高い値を使用したため、拡張は非常に重い(13回の反復、最適な結果を得るために厳選)。
  4. opencv findContours関数を使用して、結果の画像でオブジェクトの輪郭を識別しました。
  5. ドリューバウンディングボックス各輪郭オブジェクトの外接(長方形) -それらの各々は、テキストのブロックフレーム。
  6. 上記のアルゴリズムは交差またはネストされたオブジェクト(最初のカードの上部領域全体など)も見つけることができるため、必要に応じて、サイズを指定すると、検索対象のオブジェクトではない可能性のある領域(テキストブロックなど)を破棄できます。あなたの目的のために興味がない。

以下は、pyopencvを使用してpythonで記述されたコードです。C++への移植は簡単です。

import cv2

image = cv2.imread("card.png")
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # grayscale
_,thresh = cv2.threshold(gray,150,255,cv2.THRESH_BINARY_INV) # threshold
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
dilated = cv2.dilate(thresh,kernel,iterations = 13) # dilate
_, contours, hierarchy = cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) # get contours

# for each contour found, draw a rectangle around it on original image
for contour in contours:
    # get rectangle bounding contour
    [x,y,w,h] = cv2.boundingRect(contour)

    # discard areas that are too large
    if h>300 and w>300:
        continue

    # discard areas that are too small
    if h<40 or w<40:
        continue

    # draw rectangle around contour on original image
    cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,255),2)

# write original image with added contours to disk  
cv2.imwrite("contoured.jpg", image) 

元の画像は、投稿の最初の画像です。

前処理後(グレースケール、しきい値、膨張-ステップ3の後)、画像は次のようになります。

膨張した画像

以下は結果の画像です(最後の行の "contoured.jpg")。画像内のオブジェクトの最終的な境界ボックスは次のようになります。

ここに画像の説明を入力してください

左側のテキストブロックが、周囲から区切られた別のブロックとして検出されていることがわかります。

同じパラメーターを使用して同じスクリプトを使用すると(以下で説明するように、2番目のイメージに対して変更されたしきい値のタイプを除く)、他の2つのカードの結果は次のとおりです。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

パラメータの調整

パラメーター(しきい値、膨張パラメーター)は、この画像とこのタスク(テキストブロックの検索)向けに最適化されており、必要に応じて、他のカード画像や他の種類のオブジェクトを検出できるように調整できます。

しきい値処理(ステップ2)には、黒のしきい値を使用しました。投稿の2番目の画像など、テキストが背景よりも明るい画像の場合は、白いしきい値を使用する必要があるため、しきい値のタイプをcv2.THRESH_BINARY)に置き換えます。2番目の画像についても、しきい値に少し高い値(180)を使用しました。しきい値のパラメーターと拡張の反復回数を変更すると、画像内のオブジェクトの境界設定で感度の度合いが異なります。

他のオブジェクトタイプの検索:

たとえば、最初の画像で膨張を5反復に減らすと、画像内のオブジェクトの区切りが細かくなり、画像内のすべての単語が(テキストブロックではなく)大まかに見つかります。

ここに画像の説明を入力してください

単語のおおよそのサイズがわかっているため、ここでは結果が小さすぎる(幅または高さが20ピクセル未満)または大きすぎる(幅または高さが100ピクセルを超える)ので、単語である可能性が低いオブジェクトを無視する領域を破棄しました。上の画像。


2
あなたは素晴らしいです!朝にしてみます。
クリップ

興味のないオブジェクトを破棄するための別のステップを追加しました。単語やその他の種類のオブジェクト(テキストのブロック以外)を識別する例も追加
anana

詳細な回答に感謝しますが、でエラーが発生しcv2.findContoursます。それは言うValueError: too many values to unpack
Abhijith 2017年

1
問題は、関数でcv2.findContours戻り3つの引数、および元のコードのキャプチャのみ2
アブヒジス

バージョン2の@Abhijith cv2は2つの引数を返しましたが、バージョン3では3を返します
Tomasz Giba

27

@dhanushkaのアプローチは最も有望でしたが、Pythonで遊んでみたかったので、楽しみに翻訳しました。

import cv2
import numpy as np
from cv2 import boundingRect, countNonZero, cvtColor, drawContours, findContours, getStructuringElement, imread, morphologyEx, pyrDown, rectangle, threshold

large = imread(image_path)
# downsample and use it for processing
rgb = pyrDown(large)
# apply grayscale
small = cvtColor(rgb, cv2.COLOR_BGR2GRAY)
# morphological gradient
morph_kernel = getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
grad = morphologyEx(small, cv2.MORPH_GRADIENT, morph_kernel)
# binarize
_, bw = threshold(src=grad, thresh=0, maxval=255, type=cv2.THRESH_BINARY+cv2.THRESH_OTSU)
morph_kernel = getStructuringElement(cv2.MORPH_RECT, (9, 1))
# connect horizontally oriented regions
connected = morphologyEx(bw, cv2.MORPH_CLOSE, morph_kernel)
mask = np.zeros(bw.shape, np.uint8)
# find contours
im2, contours, hierarchy = findContours(connected, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# filter contours
for idx in range(0, len(hierarchy[0])):
    rect = x, y, rect_width, rect_height = boundingRect(contours[idx])
    # fill the contour
    mask = drawContours(mask, contours, idx, (255, 255, 2555), cv2.FILLED)
    # ratio of non-zero pixels in the filled region
    r = float(countNonZero(mask)) / (rect_width * rect_height)
    if r > 0.45 and rect_height > 8 and rect_width > 8:
        rgb = rectangle(rgb, (x, y+rect_height), (x+rect_width, y), (0,255,0),3)

次に、画像を表示します。

from PIL import Image
Image.fromarray(rgb).show()

ほとんどのPythonicスクリプトではありませんが、読者が理解できるように、元のC ++コードにできるだけ似せるようにしました。

オリジナルとほぼ同じように動作します。元の結果に完全に類似するように改善または修正する方法を提案させていただきます。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

ここに画像の説明を入力してください


3
Pythonバージョンを提供していただきありがとうございます。多くの人はこれが便利だと思うでしょう。+1
dhanushka 2017

輪郭の塗りつぶしと描画の違いは何ですか?ここで、fillingフェーズのないコードを見つけました:stackoverflow.com/a/23556997/6837132
SarahData

@SarahM描画と塗りつぶしの一般的な違い(かなり明白だと思いますか?)、またはOpenCV APIについて具体的に質問しているかどうかはわかりません。後者の場合は、その状態のドキュメントを参照してください。drawContours「関数は、厚さが> 0の場合、画像に輪郭の輪郭を描画するか、厚さが<0の場合、輪郭で囲まれた領域を塗りつぶします。」これは、ボックスにテキストが含まれている可能性があるかどうかを判断するためにゼロ以外のピクセルの比率を確認できるようにするためです。
rtkaleta 2017

15

Chucai YiとYingli Tianが開発したこの方法を試すことができます。

また、使用可能なソフトウェア(Opencv-1.0に基づいており、Windowsプラットフォームで実行する必要があります)を共有します(ソースコードは利用できません)。画像内のすべてのテキスト境界ボックス(色の影で表示)を生成します。サンプル画像に適用すると、次の結果が得られます。

注:結果をより堅牢にするために、隣接するボックスをさらにマージできます。


更新:あなたの究極の目標は、画像内のテキストを認識することであるならば、あなたはさらにチェックアウトすることができgttextテキストとカラー画像のためのOCRフリーソフトや地上Truthingツールです。ソースコードも入手可能です。

これにより、次のような認識されたテキストを取得できます。


gttextはWindows用です。Mac / Linuxユーザーへの提案
Saghir A. Khatri

5

上記のコードJAVAバージョン:ありがとう@William

public static List<Rect> detectLetters(Mat img){    
    List<Rect> boundRect=new ArrayList<>();

    Mat img_gray =new Mat(), img_sobel=new Mat(), img_threshold=new Mat(), element=new Mat();
    Imgproc.cvtColor(img, img_gray, Imgproc.COLOR_RGB2GRAY);
    Imgproc.Sobel(img_gray, img_sobel, CvType.CV_8U, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
    //at src, Mat dst, double thresh, double maxval, int type
    Imgproc.threshold(img_sobel, img_threshold, 0, 255, 8);
    element=Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(15,5));
    Imgproc.morphologyEx(img_threshold, img_threshold, Imgproc.MORPH_CLOSE, element);
    List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(img_threshold, contours,hierarchy, 0, 1);

    List<MatOfPoint> contours_poly = new ArrayList<MatOfPoint>(contours.size());

     for( int i = 0; i < contours.size(); i++ ){             

         MatOfPoint2f  mMOP2f1=new MatOfPoint2f();
         MatOfPoint2f  mMOP2f2=new MatOfPoint2f();

         contours.get(i).convertTo(mMOP2f1, CvType.CV_32FC2);
         Imgproc.approxPolyDP(mMOP2f1, mMOP2f2, 2, true); 
         mMOP2f2.convertTo(contours.get(i), CvType.CV_32S);


            Rect appRect = Imgproc.boundingRect(contours.get(i));
            if (appRect.width>appRect.height) {
                boundRect.add(appRect);
            }
     }

    return boundRect;
}

そして、実際にこのコードを使用してください:

        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat img1=Imgcodecs.imread("abc.png");
        List<Rect> letterBBoxes1=Utils.detectLetters(img1);

        for(int i=0; i< letterBBoxes1.size(); i++)
            Imgproc.rectangle(img1,letterBBoxes1.get(i).br(), letterBBoxes1.get(i).tl(),new Scalar(0,255,0),3,8,0);         
        Imgcodecs.imwrite("abc1.png", img1);

2

@dhanushkaのソリューションのPython実装:

def process_rgb(rgb):
    hasText = False
    gray = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
    morphKernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
    grad = cv2.morphologyEx(gray, cv2.MORPH_GRADIENT, morphKernel)
    # binarize
    _, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    # connect horizontally oriented regions
    morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
    connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, morphKernel)
    # find contours
    mask = np.zeros(bw.shape[:2], dtype="uint8")
    _,contours, hierarchy = cv2.findContours(connected, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    # filter contours
    idx = 0
    while idx >= 0:
        x,y,w,h = cv2.boundingRect(contours[idx])
        # fill the contour
        cv2.drawContours(mask, contours, idx, (255, 255, 255), cv2.FILLED)
        # ratio of non-zero pixels in the filled region
        r = cv2.contourArea(contours[idx])/(w*h)
        if(r > 0.45 and h > 5 and w > 5 and w > h):
            cv2.rectangle(rgb, (x,y), (x+w,y+h), (0, 255, 0), 2)
            hasText = True
        idx = hierarchy[0][idx][0]
    return hasText, rgb

なぜマスクを使用したのですか?
SarahData 2017

1
回答が重複しています。あなたがstackoverflow.com/a/43283990/6809909での会話に貢献した場合、それはより有用だったでしょう。
rtkaleta 2017

2

これはOpenCVSharpを使用したdhanushkaからの回答の C#バージョンです

        Mat large = new Mat(INPUT_FILE);
        Mat rgb = new Mat(), small = new Mat(), grad = new Mat(), bw = new Mat(), connected = new Mat();

        // downsample and use it for processing
        Cv2.PyrDown(large, rgb);
        Cv2.CvtColor(rgb, small, ColorConversionCodes.BGR2GRAY);

        // morphological gradient
        var morphKernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new OpenCvSharp.Size(3, 3));
        Cv2.MorphologyEx(small, grad, MorphTypes.Gradient, morphKernel);

        // binarize
        Cv2.Threshold(grad, bw, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);

        // connect horizontally oriented regions
        morphKernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(9, 1));
        Cv2.MorphologyEx(bw, connected, MorphTypes.Close, morphKernel);

        // find contours
        var mask = new Mat(Mat.Zeros(bw.Size(), MatType.CV_8UC1), Range.All);
        Cv2.FindContours(connected, out OpenCvSharp.Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple, new OpenCvSharp.Point(0, 0));

        // filter contours
        var idx = 0;
        foreach (var hierarchyItem in hierarchy)
        {
            idx = hierarchyItem.Next;
            if (idx < 0)
                break;
            OpenCvSharp.Rect rect = Cv2.BoundingRect(contours[idx]);
            var maskROI = new Mat(mask, rect);
            maskROI.SetTo(new Scalar(0, 0, 0));

            // fill the contour
            Cv2.DrawContours(mask, contours, idx, Scalar.White, -1);

            // ratio of non-zero pixels in the filled region
            double r = (double)Cv2.CountNonZero(maskROI) / (rect.Width * rect.Height);
            if (r > .45 /* assume at least 45% of the area is filled if it contains text */
                 &&
            (rect.Height > 8 && rect.Width > 8) /* constraints on region size */
            /* these two conditions alone are not very robust. better to use something 
            like the number of significant peaks in a horizontal projection as a third condition */
            )
            {
                Cv2.Rectangle(rgb, rect, new Scalar(0, 255, 0), 2);
            }
        }

        rgb.SaveImage(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rgb.jpg"));

0

これはEmguCVを使用したdhanushkaからの回答の VB.NETバージョンです。

EmguCVのいくつかの関数と構造は、OpenCVSharpを使用するC#バージョンとは異なる考慮が必要です。

Imports Emgu.CV
Imports Emgu.CV.Structure
Imports Emgu.CV.CvEnum
Imports Emgu.CV.Util

        Dim input_file As String = "C:\your_input_image.png"
        Dim large As Mat = New Mat(input_file)
        Dim rgb As New Mat
        Dim small As New Mat
        Dim grad As New Mat
        Dim bw As New Mat
        Dim connected As New Mat
        Dim morphanchor As New Point(0, 0)

        '//downsample and use it for processing
        CvInvoke.PyrDown(large, rgb)
        CvInvoke.CvtColor(rgb, small, ColorConversion.Bgr2Gray)

        '//morphological gradient
        Dim morphKernel As Mat = CvInvoke.GetStructuringElement(ElementShape.Ellipse, New Size(3, 3), morphanchor)
        CvInvoke.MorphologyEx(small, grad, MorphOp.Gradient, morphKernel, New Point(0, 0), 1, BorderType.Isolated, New MCvScalar(0))

        '// binarize
        CvInvoke.Threshold(grad, bw, 0, 255, ThresholdType.Binary Or ThresholdType.Otsu)

        '// connect horizontally oriented regions
        morphKernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, New Size(9, 1), morphanchor)
        CvInvoke.MorphologyEx(bw, connected, MorphOp.Close, morphKernel, morphanchor, 1, BorderType.Isolated, New MCvScalar(0))

        '// find contours
        Dim mask As Mat = Mat.Zeros(bw.Size.Height, bw.Size.Width, DepthType.Cv8U, 1)  '' MatType.CV_8UC1
        Dim contours As New VectorOfVectorOfPoint
        Dim hierarchy As New Mat

        CvInvoke.FindContours(connected, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple, Nothing)

        '// filter contours
        Dim idx As Integer
        Dim rect As Rectangle
        Dim maskROI As Mat
        Dim r As Double
        For Each hierarchyItem In hierarchy.GetData
            rect = CvInvoke.BoundingRectangle(contours(idx))
            maskROI = New Mat(mask, rect)
            maskROI.SetTo(New MCvScalar(0, 0, 0))

            '// fill the contour
            CvInvoke.DrawContours(mask, contours, idx, New MCvScalar(255), -1)

            '// ratio of non-zero pixels in the filled region
            r = CvInvoke.CountNonZero(maskROI) / (rect.Width * rect.Height)

            '/* assume at least 45% of the area Is filled if it contains text */
            '/* constraints on region size */
            '/* these two conditions alone are Not very robust. better to use something 
            'Like the number of significant peaks in a horizontal projection as a third condition */
            If r > 0.45 AndAlso rect.Height > 8 AndAlso rect.Width > 8 Then
                'draw green rectangle
                CvInvoke.Rectangle(rgb, rect, New MCvScalar(0, 255, 0), 2)
            End If
            idx += 1
        Next
        rgb.Save(IO.Path.Combine(Application.StartupPath, "rgb.jpg"))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.