1
$\begingroup$

I was trying to implement skew correction method for scanned documents using the method described in this paper.

The algorithm steps:

1- Threshold the image .

2- Find the fourier transform.

3- Divide the frequency space into 4 quadrants .

4- Calculate the angle of each quadrant.

5- if detected angle >= 45 then final angle = 90 - 45 else final angle = - detected angle

It's easy to implement the first 3 steps, now I can't figure out how to detect the angle from each quadrant as this step is the most important step I also tried to read another similar resource but I can't figure it out yet .

I'm using C# with Emgu on windows 7

sample code done so far:

private static Bitmap Matrix2Bitmap(Matrix<float> matrix)
    {
        CvInvoke.cvNormalize(matrix, matrix, 0.0, 255.0, Emgu.CV.CvEnum.NORM_TYPE.CV_MINMAX, IntPtr.Zero);

        Image<Gray, float> image = new Image<Gray, float>(matrix.Size);
        matrix.CopyTo(image);

        return image.ToBitmap();
    }

    // Real part is magnitude, imaginary is phase. 
    // Here we compute log(sqrt(Re^2 + Im^2) + 1) to get the magnitude and 
    // rescale it so everything is visible
    private static Matrix<float> GetDftMagnitude(Matrix<float> fftData)
    {
        //The Real part of the Fourier Transform
        Matrix<float> outReal = new Matrix<float>(fftData.Size);
        //The imaginary part of the Fourier Transform
        Matrix<float> outIm = new Matrix<float>(fftData.Size);
        CvInvoke.cvSplit(fftData, outReal, outIm, IntPtr.Zero, IntPtr.Zero);

        CvInvoke.cvPow(outReal, outReal, 2.0);
        CvInvoke.cvPow(outIm, outIm, 2.0);

        CvInvoke.cvAdd(outReal, outIm, outReal, IntPtr.Zero);
        CvInvoke.cvPow(outReal, outReal, 0.5);

        CvInvoke.cvAddS(outReal, new MCvScalar(1.0), outReal, IntPtr.Zero); // 1 + Mag
        CvInvoke.cvLog(outReal, outReal); // log(1 + Mag)            

        return outReal;
    }

    public static Bitmap DFT(Bitmap bmp)
    {
        Image<Gray, float> image = new Image<Gray, float>(bmp);

        // Transform 1 channel grayscale image into 2 channel image
        IntPtr complexImage = CvInvoke.cvCreateImage(image.Size, Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_32F, 2);
        CvInvoke.cvSetImageCOI(complexImage, 1); // Select the channel to copy into
        CvInvoke.cvCopy(image, complexImage, IntPtr.Zero);
        CvInvoke.cvSetImageCOI(complexImage, 0); // Select all channels

        // This will hold the DFT data
        Matrix<float> forwardDft = new Matrix<float>(image.Rows, image.Cols, 2); 
        CvInvoke.cvDFT(complexImage, forwardDft, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, 0);

        CvInvoke.cvReleaseImage(ref complexImage);

        // We'll display the magnitude
        Matrix<float> forwardDftMagnitude = GetDftMagnitude(forwardDft); 
        SwitchQuadrants(ref forwardDftMagnitude); 

        // Now compute the inverse to see if we can get back the original


        return Matrix2Bitmap(forwardDftMagnitude);

    }
    public static Bitmap IDFT(Matrix<float> forwardDft)
    {
        Matrix<float> reverseDft = new Matrix<float>(forwardDft.Rows, forwardDft.Cols, 2);
        CvInvoke.cvDFT(forwardDft, reverseDft, Emgu.CV.CvEnum.CV_DXT.CV_DXT_INV_SCALE, 0);
        Matrix<float> reverseDftMagnitude = GetDftMagnitude(reverseDft);
        return Matrix2Bitmap(reverseDftMagnitude);
    }
    // We have to switch quadrants so that the origin is at the image center
    private static void SwitchQuadrants(ref Matrix<float> matrix)
    {
        int cx = matrix.Cols / 2;
        int cy = matrix.Rows / 2;

        Matrix<float> q0 = matrix.GetSubRect(new Rectangle(0, 0, cx, cy));
        Matrix<float> q1 = matrix.GetSubRect(new Rectangle(cx, 0, cx, cy));
        Matrix<float> q2 = matrix.GetSubRect(new Rectangle(0, cy, cx, cy));
        Matrix<float> q3 = matrix.GetSubRect(new Rectangle(cx, cy, cx, cy));
        Matrix<float> tmp = new Matrix<float>(q0.Size);

        q0.CopyTo(tmp);
        q3.CopyTo(q0);
        tmp.CopyTo(q3);
        q1.CopyTo(tmp);
        q2.CopyTo(q1);
        tmp.CopyTo(q2);
    }

Sample Image: enter image description here

Fourier transform output:

enter image description here

$\endgroup$

2 Answers 2

2
$\begingroup$

You can use Hough transform in the frequency domain to detect straight lines and measure their angles.

$\endgroup$
0
$\begingroup$

The angle is determined by 1) summing and averaging the fourier spectrum over a line with angle theta crossing the center of the fourier rectangle, 2) repeat 1) for every possible angle theta. 3) find the maximum of 1) and 2), and the corresponding angle theta(max) is the result. 4) skew = 90-theta(max)

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.