shimat / opencvsharp

OpenCV wrapper for .NET
Apache License 2.0
5.22k stars 1.13k forks source link

using opencvsharp for rotation/deskewing an Image is not rotating correctly #1672

Open TheRealKraytonian opened 1 month ago

TheRealKraytonian commented 1 month ago

Summary of your issue

using opencvsharp for rotation/deskewing an Image is not rotating correctly

Environment

OS Name Microsoft Windows 10 Pro System SKU LENOVO_MT_20S0_BU_Think_FM_ThinkPad T14 Gen 1 Processor Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz, 2112 Mhz, 4 Core(s), 8 Logical Total Virtual Memory 60.2 GB

What did you do when you faced the problem?

Played with the some values and given const datastructures

Example code:

 #region Rotation / Deskewing
        public static void RotationDeskewing(IFormFile file)
        {
            try
            {
                string outputPath = @"C:\Users\billionaire\Documents\Projects\name\backend\Test\rotation-deskewing.png";
                static float GetSkewAngle(Mat image)
                {
                    if (image == null)
                    {
                        Console.WriteLine($"Error: Failed to read image from .");
                        return 0;
                    }

                    Mat rotationDeskewingImage = new Mat(image.Size(), image.Depth(), image.Channels());
                    Mat gray = new Mat(image.Size(), image.Depth(), image.Channels());
                    Mat blur = new Mat(image.Size(), image.Depth(), image.Channels());
                    Mat thresh = new Mat(image.Size(), image.Depth(), image.Channels());

                    Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY);
                    Cv2.GaussianBlur(gray, blur, new OpenCvSharp.Size(9, 9), 0);
                    Cv2.Threshold(blur, thresh, 200, 255, ThresholdTypes.Binary);

                    Mat dilate = new Mat(image.Size(), image.Depth(), image.Channels());
                    Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(30, 5));

                    Cv2.Dilate(thresh, dilate, kernel, iterations: 2);

                    Point[][] contours;
                    HierarchyIndex[] hierarchy;

                    Cv2.FindContours(dilate, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);

                    List<Point[]> sortedContours = contours.OrderByDescending(cnt => Cv2.ContourArea(cnt)).ToList();

                    foreach (var c in contours)
                    {
                        Rect rect = Cv2.BoundingRect(c);
                        int h = rect.Height;
                        int w = rect.Width;
                        int x = rect.X;
                        int y = rect.Y;
                        Cv2.Rectangle(image, new OpenCvSharp.Point(x, y), new OpenCvSharp.Point(x + w, y + h), Scalar.FromRgb(0, 255, 0), 2);
                    }

                    Point[] largestContour = contours[0];

                    Console.WriteLine(largestContour.Length);

                    RotatedRect minAreaRect = Cv2.MinAreaRect(largestContour);

                    Mat newImage = new Mat(image.Size(), image.Depth(), image.Channels());

                    // Cv2.ImWrite(outputPath, newImage);

                    float angle = minAreaRect.Angle;
                    if (angle < -45)
                    {
                        angle = 90 + angle;
                    }

                    return -angle;
                }

                static Mat RotateImage(Mat cvImage, float angle)
                {

                    Mat newImage = cvImage.Clone();

                    Size size = newImage.Size();
                    int width = size.Width;
                    int height = size.Height;

                    Point2f center = new Point2f(width / 2f, height / 2f);

                    Mat rotationMatrix = Cv2.GetRotationMatrix2D(center, angle, 1.0);

                    Cv2.WarpAffine(newImage, newImage, rotationMatrix, size, InterpolationFlags.Cubic, BorderTypes.Replicate);

                    return newImage;
                }

                Mat DeskewImage(IFormFile file)
                {

                    using (var ms = new MemoryStream())
                    {
                        file.CopyTo(ms);
                        byte[] fileBytes = ms.ToArray();

                        using (Mat image = Cv2.ImDecode(fileBytes, ImreadModes.Color))
                        {

                            float angle = GetSkewAngle(image); 

                            Mat deskewedImage = RotateImage(image, -angle);

                            return deskewedImage;
                        }
                    }
                }
                Mat fixedImage = DeskewImage(file);
                Cv2.ImWrite(outputPath, fixedImage);

                Console.WriteLine($"Image successfully Deskewed and saved to '{outputPath}'.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
        #endregion

Output:

rotation-deskewing

input

What did you intend to be?

original code: https://becominghuman.ai/how-to-automatically-deskew-straighten-a-text-image-using-opencv-a0c30aed83df

fixedImage

JoanCharmant commented 2 weeks ago

Since it's a multi-step algorithm I think you should investigate a bit more to find exactly which part is causing the problem.

If the problem is in the wrapper you should be able to point to a specific function that gives an unexpected output from given inputs. Otherwise the problem could also be in your implementation.