OpenCV基于傅里叶变换进行文本的旋转校正
1 2 | string filename = "source.jpg";var src = IplImage.FromFile(filename, LoadMode.GrayScale); |
1 2 3 4 | int width = Cv.GetOptimalDFTSize(src.Width);int height = Cv.GetOptimalDFTSize(src.Height);var padded = new IplImage(width, height, BitDepth.U8, 1);//扩展后的图像,单通道Cv.CopyMakeBorder(src, padded, new CvPoint(0, 0), BorderType.Constant, CvScalar.ScalarAll(0)); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //实部、虚部(单通道)var real = new IplImage(padded.Size, BitDepth.F32, 1);var imaginary = new IplImage(padded.Size, BitDepth.F32, 1);//合成(双通道)var fourier = new IplImage(padded.Size, BitDepth.F32, 2);//图像复制到实部,虚部清零Cv.ConvertScale(padded, real);Cv.Zero(imaginary);//合并、变换、再分解Cv.Merge(real, imaginary, null, null, fourier);Cv.DFT(fourier, fourier, DFTFlag.Forward);Cv.Split(fourier, real, imaginary, null, null); |
1 2 3 4 5 6 7 8 9 10 11 12 | //计算sqrt(re^2+im^2),再存回reCv.Pow(real, real, 2.0);Cv.Pow(imaginary, imaginary, 2.0);Cv.Add(real, imaginary, real);Cv.Pow(real, real, 0.5);//计算log(1+re),存回reCv.AddS(real, CvScalar.ScalarAll(1), real);Cv.Log(real, real);//归一化Cv.Normalize(real, real, 0, 1, NormType.MinMax); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /// <summary>/// 将低频部分移动到图像中心/// </summary>/// <param name="image"></param>/// <remarks>/// 0 | 3 2 | 1/// ------- ===> -------/// 1 | 2 3 | 0/// </remarks>private static void ShiftDFT(IplImage image){ int row = image.Height; int col = image.Width; int cy = row / 2; int cx = col / 2; var q0 = image.Clone(new CvRect(0, 0, cx, cy)); //左上 var q1 = image.Clone(new CvRect(0, cy, cx, cy)); //左下 var q2 = image.Clone(new CvRect(cx, cy, cx, cy)); //右下 var q3 = image.Clone(new CvRect(cx, 0, cx, cy)); //右上 Cv.SetImageROI(image, new CvRect(0, 0, cx, cy)); q2.Copy(image); Cv.ResetImageROI(image); Cv.SetImageROI(image, new CvRect(0, cy, cx, cy)); q3.Copy(image); Cv.ResetImageROI(image); Cv.SetImageROI(image, new CvRect(cx, cy, cx, cy)); q0.Copy(image); Cv.ResetImageROI(image); Cv.SetImageROI(image, new CvRect(cx, 0, cx, cy)); q1.Copy(image); Cv.ResetImageROI(image);} |
1 2 | Cv.Normalize(real, real, 0, 255, NormType.MinMax);Cv.Threshold(real, real, 150, 255, ThresholdType.Binary); |
1 2 3 4 5 6 7 | //构造8UC1格式图像var gray = new IplImage(real.Size, BitDepth.U8, 1);Cv.ConvertScale(real, gray);//找直线var storage = Cv.CreateMemStorage();var lines = Cv.HoughLines2(gray, storage, HoughLinesMethod.Standard, 1, Cv.PI / 180, 100); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | float angel = 0f;float piThresh = (float)Cv.PI / 90;float pi2 = (float)Cv.PI / 2;for (int i = 0; i < lines.Total; ++i){ //极坐标下的点,X是极径,Y是夹角,我们只关心夹角 var p = lines.GetSeqElem<CvPoint2D32f>(i); float theta = p.Value.Y; if (Math.Abs(theta) >= piThresh && Math.Abs(theta - pi2) >= piThresh) { angel = theta; break; }}angel = angel < pi2 ? angel : (angel - (float)Cv.PI); |
1 2 3 4 5 6 | if (angel != pi2){ float angelT = (float)(src.Height * Math.Tan(angel) / src.Width); angel = (float)Math.Atan(angelT);}float angelD = angel * 180 / (float)Cv.PI; |
1 2 3 4 5 6 7 8 9 10 11 12 13 | var center = new CvPoint2D32f(src.Width / 2.0, src.Height / 2.0);//图像中心var rotMat = Cv.GetRotationMatrix2D(center, angelD, 1.0);//构造仿射变换矩阵var dst = new IplImage(src.Size, BitDepth.U8, 1);//执行变换,产生的空白部分用255填充,即纯白Cv.WarpAffine(src, dst, rotMat, Interpolation.Cubic | Interpolation.FillOutliers, CvScalar.ScalarAll(255));//展示using (var win = new CvWindow("Rotation")){ win.Image = dst; Cv.WaitKey();} |
using System;using System.Collections.Generic;using System.IO;using System.Text;using OpenCvSharp;using OpenCvSharp.Extensions;using OpenCvSharp.Utilities;namespace OpenCvTest{class Program{static void Main(string[] args){//以灰度方式读入原文件string filename = "source.jpg";var src = IplImage.FromFile(filename, LoadMode.GrayScale);//转换到合适的大小,以适应快速变换int width = Cv.GetOptimalDFTSize(src.Width);int height = Cv.GetOptimalDFTSize(src.Height);var padded = new IplImage(width, height, BitDepth.U8, 1);Cv.CopyMakeBorder(src, padded, new CvPoint(0, 0), BorderType.Constant, CvScalar.ScalarAll(0));//实部、虚部(单通道)var real = new IplImage(padded.Size, BitDepth.F32, 1);var imaginary = new IplImage(padded.Size, BitDepth.F32, 1);//合并(双通道)var fourier = new IplImage(padded.Size, BitDepth.F32, 2);//图像复制到实部,虚部清零Cv.ConvertScale(padded, real);Cv.Zero(imaginary);//合并、变换、再分解Cv.Merge(real, imaginary, null, null, fourier);Cv.DFT(fourier, fourier, DFTFlag.Forward);Cv.Split(fourier, real, imaginary, null, null);//计算sqrt(re^2+im^2),再存回reCv.Pow(real, real, 2.0);Cv.Pow(imaginary, imaginary, 2.0);Cv.Add(real, imaginary, real);Cv.Pow(real, real, 0.5);//计算log(1+re),存回reCv.AddS(real, CvScalar.ScalarAll(1), real);Cv.Log(real, real);//归一化,落入0-255范围Cv.Normalize(real, real, 0, 255, NormType.MinMax);//把低频移动到中心ShiftDFT(real);//二值化,以150作为分界点,经验值,需要根据实际情况调整Cv.Threshold(real, real, 150, 255, ThresholdType.Binary);//由于HoughLines2方法只接受8UC1格式的图片,因此进行转换var gray = new IplImage(real.Size, BitDepth.U8, 1);Cv.ConvertScale(real, gray);//找直线,threshold参数取100,经验值,需要根据实际情况调整var storage = Cv.CreateMemStorage();var lines = Cv.HoughLines2(gray, storage, HoughLinesMethod.Standard, 1, Cv.PI / 180, 100);//找到符合条件的那条斜线float angel = 0f;float piThresh = (float)Cv.PI / 90;float pi2 = (float)Cv.PI / 2;for (int i = 0; i < lines.Total; ++i){//极坐标下的点,X是极径,Y是夹角,我们只关心夹角var p = lines.GetSeqElem<CvPoint2D32f>(i);float theta = p.Value.Y;if (Math.Abs(theta) >= piThresh && Math.Abs(theta - pi2) >= piThresh){angel = theta;break;}}angel = angel < pi2 ? angel : (angel - (float)Cv.PI);Cv.ReleaseMemStorage(storage);//转换角度if (angel != pi2){float angelT = (float)(src.Height * Math.Tan(angel) / src.Width);angel = (float)Math.Atan(angelT);}float angelD = angel * 180 / (float)Cv.PI;Console.WriteLine("angtlD = {0}", angelD);//旋转var center = new CvPoint2D32f(src.Width / 2.0, src.Height / 2.0);var rotMat = Cv.GetRotationMatrix2D(center, angelD, 1.0);var dst = new IplImage(src.Size, BitDepth.U8, 1);Cv.WarpAffine(src, dst, rotMat, Interpolation.Cubic | Interpolation.FillOutliers, CvScalar.ScalarAll(255));//显示using (var window = new CvWindow("Image")){window.Image = src;using (var win2 = new CvWindow("Dest")){win2.Image = dst;Cv.WaitKey();}}}/// <summary>/// 将低频部分移动到图像中心/// </summary>/// <param name="image"></param>/// <remarks>/// 0 | 3 2 | 1/// ------- ===> -------/// 1 | 2 3 | 0/// </remarks>private static void ShiftDFT(IplImage image){int row = image.Height;int col = image.Width;int cy = row / 2;int cx = col / 2;var q0 = image.Clone(new CvRect(0, 0, cx, cy));//左上var q1 = image.Clone(new CvRect(0, cy, cx, cy));//左下var q2 = image.Clone(new CvRect(cx, cy, cx, cy));//右下var q3 = image.Clone(new CvRect(cx, 0, cx, cy));//右上Cv.SetImageROI(image, new CvRect(0, 0, cx, cy));q2.Copy(image);Cv.ResetImageROI(image);Cv.SetImageROI(image, new CvRect(0, cy, cx, cy));q3.Copy(image);Cv.ResetImageROI(image);Cv.SetImageROI(image, new CvRect(cx, cy, cx, cy));q0.Copy(image);Cv.ResetImageROI(image);Cv.SetImageROI(image, new CvRect(cx, 0, cx, cy));q1.Copy(image);Cv.ResetImageROI(image);}}}
附件列表





浙公网安备 33010602011771号