Ask Your Question
1

How find document on scanned image? OpenCV

asked 2014-10-01 01:01:18 -0600

Viton-Zizu gravatar image

updated 2014-10-01 22:52:19 -0600

May be anybody know, how i can find document on scanned image? Use OpenCV, C++ or C#! Try find squares, but this not work! Attach picture for example, i need crop this red rectangle from picture image description ` static CvPoint[] FindSquare(IplImage img, CvMemStorage storage) { const int N = 15;//11

        CvSize sz = new CvSize(img.Width & -2, img.Height & -2);
        IplImage timg = img.Clone(); // make a copy of input image
        //Draw frame in begin of image for best square finding near edge of big image
        timg.DrawRect(0, 0, timg.Width, timg.Height, CvColor.Black, 2);
        IplImage gray = new IplImage(sz, BitDepth.U8, 1);
        IplImage pyr = new IplImage(sz.Width / 2, sz.Height / 2, BitDepth.U8, 3);
        // create empty sequence that will contain points -
        // 4 points per square (the square's vertices)
        CvSeq<CvPoint> squares = new CvSeq<CvPoint>(SeqType.Zero, CvSeq.SizeOf, storage);
        // select the maximum ROI in the image
        // with the width and height divisible by 2
        timg.ROI = new CvRect(0, 0, sz.Width, sz.Height);

        // down-Scale and upscale the image to filter out the noise
        Cv.PyrDown(timg, pyr, CvFilter.Gaussian5x5);
        Cv.PyrUp(pyr, timg, CvFilter.Gaussian5x5);
        IplImage tgray = new IplImage(sz, BitDepth.U8, 1);

            // extract the c-th color plane
            timg.COI = 1;
            Cv.Copy(timg, tgray, null);

            // try several threshold levels
            for (int l = 1; l < N; l++)
            {
                // hack: use Canny instead of zero threshold level.
                // Canny helps to catch squares with gradient shading   
                if (l == 0)
                {
                    // apply Canny. Take the upper threshold from slider
                    // and set the lower to 0 (which forces edges merging) 
                    Cv.Canny(tgray, gray, 0, Thresh, ApertureSize.Size5);
                    // dilate canny output to remove potential
                    // holes between edge segments 
                    Cv.Dilate(gray, gray, null, 1);
                }
                else
                {
                    // apply threshold if l!=0:
                    //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0                        
                    Cv.Threshold(tgray, gray, (l + 1) * 255.0 / N, 255, ThresholdType.Binary);
                }

                // find contours and store them all as a list
                CvSeq<CvPoint> contours;
                Cv.FindContours(gray, storage, out contours, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxSimple, new CvPoint(0, 0));
                // test each contour
                while (contours != null)
                {
                    // approximate contour with accuracy proportional
                    // to the contour perimeter
                    CvSeq<CvPoint> result = Cv.ApproxPoly(contours, CvContour.SizeOf, storage, ApproxPolyMethod.DP, contours.ContourPerimeter() * 0.02, false);
                    // square contours should have 4 vertices after approximation
                    // relatively large area (to filter out noisy contours)
                    // and be convex.
                    // Note: absolute value of an area is used because
                    // area may be positive or negative - in accordance with the
                    // contour orientation
                    if (result.Total == 4 && Math.Abs(result.ContourArea(CvSlice.WholeSeq)) > 1000 && result.CheckContourConvexity())
                    {
                        double s = 0;                            
                        for (int i = 0; i < 5; i++)
                        {
                            // find minimum Angle between joint
                            // edges (maximum of cosine)
                            if (i >= 2)
                            {
                                double t = Math.Abs(Angle(result[i].Value, result[i - 2].Value, result[i - 1].Value));
                                s = s > t ? s : t;
                            }
                        }

                        // if cosines of all angles are small
                        // (all angles are ~90 degree) then write quandrange
                        // vertices to resultant sequence 
                        if (s < 0.3)
                        {
                            for (int i = 0; i < 4; i++)
                            {
                                //My squares. Then sort ...
(more)
edit retag flag offensive close merge delete

Comments

3

Have you had a look a the thresholding functions? docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold could be a start. What have you tried? How do your current results look like?

FooBar gravatar imageFooBar ( 2014-10-01 01:53:09 -0600 )edit

Thx for response! My current result: http://i004.radikal.ru/1410/19/768f088b5a77.jpg I can find red area or green area, different for the same documents! How i can find blue area? Or stable red or green area for the same documents!? Code attached!

Viton-Zizu gravatar imageViton-Zizu ( 2014-10-01 22:43:10 -0600 )edit

Start by applying a blur filter, then a canny edge detector. Cluster all the white dots and apply a convex hull function to that. It will match perfectly to your paper with such a nice background!

StevenPuttemans gravatar imageStevenPuttemans ( 2014-10-02 03:33:48 -0600 )edit

Great! But how i can cluster all the white dots!?

Viton-Zizu gravatar imageViton-Zizu ( 2014-10-02 23:40:09 -0600 )edit

Look at the convex hull function which you can apply on a binary image!

StevenPuttemans gravatar imageStevenPuttemans ( 2014-10-03 01:17:37 -0600 )edit

I understand, but how i can get points for convex hull function? (in parametr)

Viton-Zizu gravatar imageViton-Zizu ( 2014-10-06 02:04:52 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
1

answered 2014-10-06 03:18:10 -0600

updated 2014-10-06 03:21:51 -0600

O forgive me I forgot a step. After making it a binary image on dynamic thresholding, look for straight lines first, then apply a convex hull on that set of lines. Something like this code snippet should do the trick, adapting filters to your needs.

// Grayscale image and smoothing
Mat grayscale;
cvtColor(original, grayscale, CV_RGB2GRAY);
GaussianBlur(grayscale, grayscale, Size(5,5), 0, 0);
imshow("grayscale blur gaussian", grayscale);

// Adaptive thresholding
Mat adaptive;
adaptiveThreshold(grayscale, adaptive, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 5, 4);
// REMARK --> inverse image needed for HoughLines
bitwise_not(adaptive, adaptive);
imshow("adaptive threshold", adaptive);

// Parameters for convex hull definement
vector<Point> pointset;

// Calculate Hough lines
Mat show = original.clone();
vector<Vec4i> lines;
HoughLinesP(adaptive, lines, 1, CV_PI/180, 70, 30, 10);
for( size_t i = 0; i < lines.size(); i++ )
{
    line( show, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 1, 8 );
    // Sla ook telkens het begin en eindpunt op van de lijn, want die moet uiteindelijk zeker in de convex hull zitten!
    pointset.push_back(Point(lines[i][0], lines[i][1]));
    pointset.push_back(Point(lines[i][2], lines[i][3]));
}
imshow("Hough Lines", show);

// Calculate convex hull
vector<Point> convex_hull;
convexHull(pointset, convex_hull);
edit flag offensive delete link more

Comments

Unfortunatly, but i try this snippet, it cut corners of document sometimes. And dont work if document inside cover! Like this:

Viton-Zizu gravatar imageViton-Zizu ( 2014-11-14 01:22:26 -0600 )edit
Viton-Zizu gravatar imageViton-Zizu ( 2014-11-14 01:37:05 -0600 )edit

Question Tools

Stats

Asked: 2014-10-01 01:01:18 -0600

Seen: 3,276 times

Last updated: Oct 06 '14