web-dev-qa-db-ja.com

写真の紙の角を検出するアルゴリズム

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

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

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

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

より広いコンテキスト:

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

アルゴリズムで処理したいサンプル画像を以下に示します。挑戦したい場合、大きな画像は http://madteckhead.com/tmp にあります。

case 1
(ソース: madteckhead.com

case 2
(ソース: madteckhead.com

case 3
(ソース: madteckhead.com

case 4
(ソース: madteckhead.com

最良の場合、これにより以下が得られます。

case 1 - canny
(ソース: madteckhead.com

case 1 - post canny
(ソース: madteckhead.com

case 1 - largest blog
(ソース: madteckhead.com

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

case 2 - canny
(ソース: madteckhead.com

case 2 - post canny
(ソース: madteckhead.com

case 2 - largest blog
(ソース: madteckhead.com

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

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

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

case 1 case 2case 3case 4
(ソース: madteckhead.com

90
Nathan Keller

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

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

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

Houghline2()変換の場合、_CV_HOUGH_STANDARD_ではなく_CV_HOUGH_PROBABILISTIC_を試してください。rhoおよびthetaが返されます。極座標でラインを定義し、特定の許容範囲内でラインをグループ化できます。

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

その後、平行線または線間の距離をより簡単に分析できます。

お役に立てれば。

26
Daniel Crowley

私の大学の学生グループは最近、まさにこれを行うために書かれたiPhoneアプリ(およびpython OpenCVアプリ)をデモしました。

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

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

17
Martin Foot

少し実験を重ねた結果、次のようになりました。

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 __== '__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()

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

1234

16
Vanuan

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

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

enter image description here

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

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

result

4
Flayn

ここに、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);
3
GBF_Gabriel

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

3
Hephaestus
  1. 次に、クラスターの1つで輪郭またはハフを使用します(内部)

  2. Use kmeans segment 2 cluster

  3. Then use contours or hough on one of the clusters (intenral)
1
user3452134