これは Stackoverflow に関する私の最初の質問です。
私はプロのソフトウェア エンジニア (Java、C#) であり、画像処理と Android 関連のテクノロジに関する知識はありません。私は修士論文用の Android アプリケーションを作成しており、私の国の視覚障害者が Android スマートフォンから母国語でドキュメントを読むことをサポートしています。
ドキュメントのサンプル サイズとして A4 を選択しました。A4 ドキュメント全体がカメラの視野に入ると、アプリは最終的にドキュメントに自動的にフォーカスし (音声通知がユーザーに与えられる必要があります)、その画像をキャプチャする必要があります。次に、文書を tesseract エンジンで実行して OCR に変換する予定です。(他の人がこのアプリケーションのテキスト読み上げ部分を行っています)
いくつかのアプリケーションを徹底的にグーグル検索し、OpenCV のドキュメントを思いつきました。http://docs.opencv.org/opencv_tutorials.pdfは、「輪郭の境界ボックスと円の作成」について説明しており、私の命の恩人になるようです。
私の MSC プロジェクトは 300 時間のパートタイム プロジェクトなので、OpenCV を学ぶために自分で C++/Python の例を Java に変換するのに時間を費やした後、何も残らないのではないかと心配しています。私は JavaCV も試しましたが、まだ成長段階にあるように見えるので、おそらく自分でサンプルを変換する必要があります。
私が専門家に聞きたかったのは、OpenCV で本当にこのようなことができるのかということです。前もって感謝します!
編集。コメントのリンクを見て、C++ の例を Java に移植しようとしました。これが私がこれまでに得たものです。それでもやるべきことがいくつかありますが...
int thresh = 50, N = 11;
// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
static double angle(Point pt1, Point pt2, Point pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/Math.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
public void find_squares(Mat image, Vector<Vector<Point> > squares)
{
Imgproc img = new Imgproc();
// blur will enhance edge detection
org.opencv.core.Mat blurred = new org.opencv.core.Mat();
Imgproc.medianBlur(image, blurred, 9);
Mat gray0 = new Mat(blurred.size(), CvType.CV_8U);
Mat gray = new Mat();
// Vector<Vector<Point> > contours;
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
int ch[] = {c, 0};
// Core.mixChannels(blurred, 1, gray0, 1, ch, 1);
List<Mat> src = new ArrayList<Mat>();
src.add(blurred);
List<Mat> dest = new ArrayList<Mat>();
dest.add(gray0);
MatOfInt a = new MatOfInt(ch);
Core.mixChannels(src, dest, a);
// try several threshold levels
final int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Imgproc.Canny(gray0, gray, 10, 20, 3, false);
// Dilate helps to remove potential holes between edge segments
Point point = new Point(-1, -1);
Imgproc.dilate(gray, gray, new Mat(), point, 1);
}
else
{
// TODO
// gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list. //TODO
Imgproc.findContours(gray, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// Test contours
MatOfPoint2f approx = new MatOfPoint2f();
for (int i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
double epilson = Imgproc.arcLength(new MatOfPoint2f(contours.get(i)), true);
epilson *= 0.02;
Imgproc.approxPolyDP(new MatOfPoint2f(contours.get(i)), approx, epilson, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
// Mat mmm = new Mat();
// MatOfPoint ppp = new MatOfPoint();
if (/*TODO*/approx.size().area() == 4 &&
Math.abs(Imgproc.contourArea(approx)) > 1000 &&
Imgproc.isContourConvex(/*TODO*/approx))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = Math.abs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = /*TODO*/MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares./*TODO*/push_back(approx);
}
}
}
}
}
}