Deskew扫描图像

时间:2010-03-06 23:03:06

标签: c# image-manipulation

我在OMR项目中工作,我们正在使用C#。当我们来扫描答案纸时,图像会歪斜。我们怎么能纠正他们?

4 个答案:

答案 0 :(得分:9)

VB.Net代码可用here,但是因为你要求C#这里是他们的Deskew类的C#翻译(注意:Binarize(严格来说没有必要,但工作得更好)和Rotate是练习留给用户)。

 public class Deskew
 {
        // Representation of a line in the image.  
        private class HougLine
        {
            // Count of points in the line.
            public int Count;
            // Index in Matrix.
            public int Index;
            // The line is represented as all x,y that solve y*cos(alpha)-x*sin(alpha)=d
            public double Alpha;
        }


        // The Bitmap
        Bitmap _internalBmp;

        // The range of angles to search for lines
        const double ALPHA_START = -20;
        const double ALPHA_STEP = 0.2;
        const int STEPS = 40 * 5;
        const double STEP = 1;

        // Precalculation of sin and cos.
        double[] _sinA;
        double[] _cosA;

        // Range of d
        double _min;


        int _count;
        // Count of points that fit in a line.
        int[] _hMatrix;

        public Bitmap DeskewImage(Bitmap image, int type, int binarizeThreshold)
        {
            Size oldSize = image.Size;

            _internalBmp = BitmapFunctions.Resize(image, new Size(1000, 1000), true, image.PixelFormat);
            Binarize(_internalBmp, binarizeThreshold);


            return Rotate(image, GetSkewAngle());
        }

        // Calculate the skew angle of the image cBmp.
        private double GetSkewAngle()
        {
            // Hough Transformation
            Calc();

            // Top 20 of the detected lines in the image.
            HougLine[] hl = GetTop(20);

            // Average angle of the lines
            double sum = 0;
            int count = 0;
            for (int i = 0; i <= 19; i++)
            {
                sum += hl[i].Alpha;
                count += 1;
            }
            return sum / count;
        }

        // Calculate the Count lines in the image with most points.
        private HougLine[] GetTop(int count)
        {
            HougLine[] hl = new HougLine[count];

            for (int i = 0; i <= count - 1; i++)
            {
                hl[i] = new HougLine();
            }
            for (int i = 0; i <= _hMatrix.Length - 1; i++)
            {
                if (_hMatrix[i] > hl[count - 1].Count)
                {
                    hl[count - 1].Count = _hMatrix[i];
                    hl[count - 1].Index = i;
                    int j = count - 1;
                    while (j > 0 && hl[j].Count > hl[j - 1].Count)
                    {
                        HougLine tmp = hl[j];
                        hl[j] = hl[j - 1];
                        hl[j - 1] = tmp;
                        j -= 1;
                    }
                }
            }

            for (int i = 0; i <= count - 1; i++)
            {
                int dIndex = hl[i].Index / STEPS;
                int alphaIndex = hl[i].Index - dIndex * STEPS;
                hl[i].Alpha = GetAlpha(alphaIndex);
                //hl[i].D = dIndex + _min;
            }

            return hl;
        }


        // Hough Transforamtion:
        private void Calc()
        {
            int hMin = _internalBmp.Height / 4;
            int hMax = _internalBmp.Height * 3 / 4;

            Init();
            for (int y = hMin; y <= hMax; y++)
            {
                for (int x = 1; x <= _internalBmp.Width - 2; x++)
                {
                    // Only lower edges are considered.
                    if (IsBlack(x, y))
                    {
                        if (!IsBlack(x, y + 1))
                        {
                            Calc(x, y);
                        }
                    }
                }
            }
        }

        // Calculate all lines through the point (x,y).
        private void Calc(int x, int y)
        {
            int alpha;

            for (alpha = 0; alpha <= STEPS - 1; alpha++)
            {
                double d = y * _cosA[alpha] - x * _sinA[alpha];
                int calculatedIndex = (int)CalcDIndex(d);
                int index = calculatedIndex * STEPS + alpha;
                try
                {
                    _hMatrix[index] += 1;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.ToString());
                }
            }
        }
        private double CalcDIndex(double d)
        {
            return Convert.ToInt32(d - _min);
        }
        private bool IsBlack(int x, int y)
        {
            Color c = _internalBmp.GetPixel(x, y);
            double luminance = (c.R * 0.299) + (c.G * 0.587) + (c.B * 0.114);
            return luminance < 140;
        }

        private void Init()
        {
            // Precalculation of sin and cos.
            _cosA = new double[STEPS];
            _sinA = new double[STEPS];

            for (int i = 0; i < STEPS; i++)
            {
                double angle = GetAlpha(i) * Math.PI / 180.0;
                _sinA[i] = Math.Sin(angle);
                _cosA[i] = Math.Cos(angle);
            }

            // Range of d:            
            _min = -_internalBmp.Width;
            _count = (int)(2 * (_internalBmp.Width + _internalBmp.Height) / STEP);
            _hMatrix = new int[_count * STEPS];


        }

        private static double GetAlpha(int index)
        {
            return ALPHA_START + index * ALPHA_STEP;
        }
}

答案 1 :(得分:2)

扫描文档始终倾斜平均[-10; +10]度角。 使用霍夫变换很容易对它们进行校正,就像Lou Franco说的那样。此变换可检测图像上的几个角度的线条。您只需在文档水平线上选择相应的一个,然后旋转它。

  1. 尝试隔离与文档水平线对应的像素(例如,底部有白色像素的黑色像素)。

  2. 运行Hough变换。不要忘记在C#中使用“不安全”模式,通过使用指针来固定整个图像的过程。

  3. 以找到的相反角度旋转文档。
  4. 就像二进制文档上的魅力一样(很容易扩展到灰度级文档)

答案 2 :(得分:1)

免责声明:我在Atalasoft工作,DotImage Document Imaging可以使用几行代码执行此操作。

Deskew是一个艺术术语,描述了你想要做的事情。正如Ben Voigt所说,它在技术上是旋转,而不是倾斜 - 但是,如果你搜索,你会发现自动偏移校正下的算法。

执行此操作的常规方法是在图像中执行hough transform to look for the prevalent lines。使用普通文档时,其中许多文档将与纸张的两侧正交。

答案 3 :(得分:0)

你确定它是“倾斜”而不是“旋转”(旋转保持角度,倾斜没有)。

  • 使用某种注册标记(至少在两个地方),即使在旋转时也能识别。
  • 找到这些标记的坐标并计算旋转角度。
  • 将旋转变换矩阵应用于图像。