107

写真の請求書/領収書/紙の角を検出するための最良の方法は何ですか?これは、OCRの前に、後続の遠近法補正に使用されます。

私の現在のアプローチは次のとおりです。

RGB>グレー>しきい値処理によるキャニーエッジ検出>拡張(1)>小さなオブジェクトの削除(6)>境界オブジェクトのクリア>凸領域に基づいた大きなブログの選択。>[コーナー検出-実装されていません]

このタイプのセグメンテーションを処理するには、より堅牢な「インテリジェント」/統計的アプローチが必要だと思わずにはいられません。トレーニングの例はあまりありませんが、おそらく100枚の画像をまとめることができます。

より広い文脈:

私はmatlabを使用してプロトタイプを作成しており、OpenCVとTesserect-OCRでシステムを実装することを計画しています。これは、この特定のアプリケーションのために解決する必要のある多くの画像処理の問題の最初のものです。ですから、私は自分自身のソリューションを展開し、画像処理アルゴリズムに慣れることを目指しています。

アルゴリズムで処理したいサンプル画像を次に示します。チャレンジしたい場合は、大きな画像がhttp://madteckhead.com/tmpにあります。

ケース1
(出典:madteckhead.com

ケース2
(出典:madteckhead.com

ケース3
(出典:madteckhead.com

ケース4
(出典:madteckhead.com

最良の場合、これは次のようになります。

ケース1-キャニー
(出典:madteckhead.com

ケース1-キャニーを投稿
(出典:madteckhead.com

ケース1-最大のブログ
(出典:madteckhead.com

ただし、他の場合は簡単に失敗します。

ケース2-キャニー
(出典:madteckhead.com

ケース2-キャニーを投稿
(出典:madteckhead.com

ケース2-最大のブログ
(出典:madteckhead.com

すべての素晴らしいアイデアを事前に感謝します!私はSOが大好きです!

編集:ハフ変換の進捗状況

Q:コーナーを見つけるためにハフラインをクラスター化するアルゴリズムは何ですか?回答からのアドバイスに従って、ハフ変換を使用し、線を選択して、それらをフィルタリングすることができました。私の現在のアプローチはかなり粗雑です。請求書は常に画像との位置合わせが15度未満になると想定しています。これが事実である場合、私はラインに対して妥当な結果をもたらすことになります(以下を参照)。しかし、コーナーを推定するために線をクラスター化(または投票)するための適切なアルゴリズムが完全にはわかりません。ハフ線は連続していません。また、ノイズの多い画像では、平行線が存在する可能性があるため、線の原点からの何らかの形または距離が必要です。何か案は?

ケース1 ケース2 ケース3 ケース4
(出典:madteckhead.com

4

8 に答える 8

30

私は今年初めにこれに取り組んでいたマーティンの友人です。これは私の初めてのコーディングプロジェクトであり、ちょっと急いで終わったので、コードにはエラーが必要です...デコード...あなたがすでに行っていることからいくつかのヒントを紹介します。明日の休日にコードを並べ替えます。

最初のヒント、OpenCVそしてpython素晴らしいです、できるだけ早くそれらに移動します。:D

小さなオブジェクトやノイズを削除する代わりに、キャニー拘束を下げて、より多くのエッジを受け入れ、最大の閉じた輪郭を見つけます(OpenCVfindcontour()でいくつかの単純なパラメーターを使用して使用したと思いますCV_RETR_LIST)。それが白い紙の上にあるときはまだ苦労するかもしれませんが、間違いなく最高の結果を提供していました。

Houghline2()変換では、ではなくを試してみてください。rhoCV_HOUGH_STANDARDthetaCV_HOUGH_PROBABILISTICが与えられ、極座標で線が定義されます。次に、それらに対して一定の許容範囲内で線をグループ化できます。

私のグループ化はルックアップテーブルとして機能しました。ハフ変換から出力された各行に対して、rhoとthetaのペアが得られます。これらの値がテーブル内の値のペアの5%以内である場合、それらは破棄され、その5%外である場合、新しいエントリがテーブルに追加されました。

その後、平行線または線間の距離の分析をはるかに簡単に行うことができます。

お役に立てれば。

于 2011-07-10T22:47:23.550 に答える
20

これが私が少し実験した後に思いついたものです:

import cv, cv2, numpy as np
import sys

def get_new(old):
    new = np.ones(old.shape, np.uint8)
    cv2.bitwise_not(new,new)
    return new

if __name__ == '__main__':
    orig = cv2.imread(sys.argv[1])

    # these constants are carefully picked
    MORPH = 9
    CANNY = 84
    HOUGH = 25

    img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
    cv2.GaussianBlur(img, (3,3), 0, img)


    # this is to recognize white on white
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
    dilated = cv2.dilate(img, kernel)

    edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)

    lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
    for line in lines[0]:
         cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                         (255,0,0), 2, 8)

    # finding contours
    contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                   cv.CV_CHAIN_APPROX_TC89_KCOS)
    contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
    contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)

    # simplify contours down to polygons
    rects = []
    for cont in contours:
        rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
        rects.append(rect)

    # that's basically it
    cv2.drawContours(orig, rects,-1,(0,255,0),1)

    # show only contours
    new = get_new(img)
    cv2.drawContours(new, rects,-1,(0,255,0),1)
    cv2.GaussianBlur(new, (9,9), 0, new)
    new = cv2.Canny(new, 0, CANNY, apertureSize=3)

    cv2.namedWindow('result', cv2.WINDOW_NORMAL)
    cv2.imshow('result', orig)
    cv2.waitKey(0)
    cv2.imshow('result', dilated)
    cv2.waitKey(0)
    cv2.imshow('result', edges)
    cv2.waitKey(0)
    cv2.imshow('result', new)
    cv2.waitKey(0)

    cv2.destroyAllWindows()

完璧ではありませんが、少なくともすべてのサンプルで機能します。

1 2 3 4

于 2013-09-20T02:42:09.933 に答える
19

私の大学の学生グループは最近、まさにこれを行うために作成したiPhoneアプリ(およびPython OpenCVアプリ)のデモを行いました。私が覚えているように、手順は次のようなものでした。

  • 紙のテキストを完全に削除するメディアンフィルター(これは、かなり良い照明の白い紙に手書きされたテキストであり、印刷されたテキストでは機能しない可能性があり、非常にうまく機能しました)。その理由は、コーナー検出がはるかに簡単になるためです。
  • ラインのハフ変換
  • ハフ変換アキュムレータ空間でピークを見つけ、画像全体に各線を描画します。
  • 線を分析し、互いに非常に近く、同じ角度にある線をすべて削除します(線を1つにクラスター化します)。これが必要なのは、ハフ変換が離散サンプル空間で機能しているため、完全ではないためです。
  • ほぼ平行で他のペアと交差するラインのペアを見つけて、どのラインがクワッドを形成するかを確認します。

これはかなりうまく機能しているようで、紙や本の写真を撮り、コーナー検出を実行してから、画像内のドキュメントをほぼリアルタイムで平面にマッピングすることができました(実行するOpenCV機能は1つだけでした)。マッピング)。私がそれが機能しているのを見たとき、OCRはありませんでした。

于 2011-07-02T08:03:24.057 に答える
10

エッジ検出から始める代わりに、コーナー検出を使用できます。

Marvin Frameworkは、この目的のためにMoravecアルゴリズムの実装を提供します。あなたは出発点として論文の角を見つけることができます。Moravecのアルゴリズムの出力の下:

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

于 2013-05-25T15:42:55.170 に答える
5

また、Sobel演算子の結果に対してMSER(最大安定極値領域)を使用して、画像の安定領域を見つけることができます。MSERによって返される領域ごとに、凸包とポリ近似を適用して、次のようなものを取得できます。

しかし、この種の検出は、常に最良の結果を返すとは限らない単一の画像よりもライブ検出に役立ちます。

結果

于 2015-08-17T10:36:57.837 に答える
3

エッジ検出後、ハフ変換を使用します。次に、それらのポイントをラベル付きのSVM(サポートベクターマシン)に配置します。例に滑らかな線が付いている場合、SVMは例の必要な部分と他の部分を簡単に分割できます。SVMに関する私のアドバイスは、接続性や長さなどのパラメーターを設定することです。つまり、ポイントが接続されていて長い場合、それらはレシートのラインである可能性があります。次に、他のすべてのポイントを削除できます。

于 2011-07-02T22:17:31.147 に答える
3

ここに、C++を使用した@Vanuanのコードがあります。

cv::cvtColor(mat, mat, CV_BGR2GRAY);
cv::GaussianBlur(mat, mat, cv::Size(3,3), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Point(9,9));
cv::Mat dilated;
cv::dilate(mat, dilated, kernel);

cv::Mat edges;
cv::Canny(dilated, edges, 84, 3);

std::vector<cv::Vec4i> lines;
lines.clear();
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 25);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
    cv::Vec4i l = *it;
    cv::line(edges, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255,0,0), 2, 8);
}
std::vector< std::vector<cv::Point> > contours;
cv::findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);
std::vector< std::vector<cv::Point> > contoursCleaned;
for (int i=0; i < contours.size(); i++) {
    if (cv::arcLength(contours[i], false) > 100)
        contoursCleaned.push_back(contours[i]);
}
std::vector<std::vector<cv::Point> > contoursArea;

for (int i=0; i < contoursCleaned.size(); i++) {
    if (cv::contourArea(contoursCleaned[i]) > 10000){
        contoursArea.push_back(contoursCleaned[i]);
    }
}
std::vector<std::vector<cv::Point> > contoursDraw (contoursCleaned.size());
for (int i=0; i < contoursArea.size(); i++){
    cv::approxPolyDP(Mat(contoursArea[i]), contoursDraw[i], 40, true);
}
Mat drawing = Mat::zeros( mat.size(), CV_8UC3 );
cv::drawContours(drawing, contoursDraw, -1, cv::Scalar(0,255,0),1);
于 2013-09-29T21:10:29.237 に答える
1
  1. ラボスペースに変換

  2. kmeansセグメント2クラスターを使用する

  3. 次に、クラスターの1つで等高線またはハフを使用します(内部)
于 2014-10-29T06:37:17.447 に答える