写真の請求書/領収書/用紙の角を検出する最良の方法は何ですか?これは、OCRの前に、その後の遠近補正に使用されます。
RGB>グレー>しきい値を使用したキャニーエッジ検出>拡張(1)>小さいオブジェクトの削除(6)>境界オブジェクトのクリア>凸領域に基づいて大きなブログを選択します。 > [コーナー検出-実装されていません]
私は仕方がありませんが、このタイプのセグメンテーションを処理するには、より堅牢な「インテリジェント」/統計的アプローチが必要だと思います。トレーニングの例はあまりありませんが、おそらく100枚の画像を一緒に取得できます。
Matlabを使用してプロトタイプを作成し、OpenCVおよびTesserect-OCRでシステムを実装することを計画しています。これは、この特定のアプリケーションのために解決する必要がある多くの画像処理の問題の最初のものです。だから、私は自分のソリューションを展開して、画像処理アルゴリズムに慣れ親しんでいます。
アルゴリズムで処理したいサンプル画像を以下に示します。挑戦したい場合、大きな画像は http://madteckhead.com/tmp にあります。
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
(ソース: madteckhead.com )
すべての素晴らしいアイデアを事前に感謝します!大好き!
Q:ハフラインをクラスター化してコーナーを見つけるアルゴリズムは何ですか?回答からのアドバイスに従って、ハフ変換を使用し、行を選択し、フィルタリングすることができました。私の現在のアプローチはかなり粗雑です。私は、請求書が常に画像と15度未満のずれになると仮定しました。これが当てはまる場合は、回線に対して妥当な結果が得られます(以下を参照)。しかし、コーナーを推定するためにラインをクラスタリング(または投票)するのに適したアルゴリズムが完全に定かではありません。ハフ線は連続していません。また、ノイズの多い画像では、平行線が存在する可能性があるため、線の原点からの何らかのメトリックまたは距離が必要です。何か案は?
(ソース: madteckhead.com )
私は今年初めにこれに取り組んでいたマーティンの友人です。これは私の初めてのコーディングプロジェクトであり、ちょっとしたラッシュで終わりました。そのため、コードには多少のエラーが必要です。..デコード...既に見たことからいくつかのヒントを示します。明日の休みにコードを並べ替えます。
最初のヒント、OpenCV
とpython
は素晴らしいです。できるだけ早くそれらに移動してください。 :D
小さなオブジェクトやノイズを削除する代わりに、キャニー拘束を下げてより多くのエッジを受け入れ、最大の閉じた輪郭を見つけます(OpenCVではいくつかの単純なパラメータでfindcontour()
を使用し、_CV_RETR_LIST
_)。白い紙の上ではまだ苦労するかもしれませんが、間違いなく最高の結果を提供していました。
Houghline2()
変換の場合、_CV_HOUGH_STANDARD
_ではなく_CV_HOUGH_PROBABILISTIC
_を試してください。rhoおよびthetaが返されます。極座標でラインを定義し、特定の許容範囲内でラインをグループ化できます。
私のグループ化はルックアップテーブルとして機能し、ハフ変換から出力された各行に対して、ローとシータのペアが得られます。これらの値がテーブル内の値のペアの5%以内にある場合、それらは破棄され、5%の外にある場合、新しいエントリがテーブルに追加されました。
その後、平行線または線間の距離をより簡単に分析できます。
お役に立てれば。
私の大学の学生グループは最近、まさにこれを行うために書かれたiPhoneアプリ(およびpython OpenCVアプリ)をデモしました。
これはかなりうまくいくようで、紙や本の写真を撮り、コーナー検出を実行してから、画像内のドキュメントをほぼリアルタイムで平面にマッピングすることができました(実行する単一のOpenCV関数がありましたマッピング)。 OCRが機能しているのを見て、OCRはありませんでした。
少し実験を重ねた結果、次のようになりました。
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()
完全ではありませんが、少なくともすべてのサンプルで機能します。
エッジ検出から開始する代わりに、コーナー検出を使用できます。
Marvin Framework は、この目的のためにMoravecアルゴリズムの実装を提供します。出発点として論文の隅を見つけることができます。 Moravecのアルゴリズムの出力の下:
また、Sobel演算子の結果に [〜#〜] mser [〜#〜] (最大安定極値領域)を使用して、画像の安定領域を見つけることができます。 MSERによって返される各領域に対して、凸包およびポリ近似を適用して、次のようなものを取得できます。
しかし、この種の検出は、常に最良の結果を返すとは限らない単一の画像以上のライブ検出に役立ちます。
ここに、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);
エッジ検出後、ハフ変換を使用します。次に、それらのポイントをラベル付きのSVM(サポートベクターマシン)に配置します。例に滑らかな線が含まれている場合、SVMは例の必要な部分と他の部分を分割するのに困難はありません。 SVMに関する私のアドバイスは、接続性や長さなどのパラメーターを設定します。つまり、ポイントが接続されていて長い場合、それらは領収書のラインである可能性があります。その後、他のすべてのポイントを削除できます。
次に、クラスターの1つで輪郭またはハフを使用します(内部)
Use kmeans segment 2 cluster