LoginSignup
1
0

OpenCVで直線検出を使って文字の傾きを水平補正する

Last updated at Posted at 2024-03-13

Tesseract5でOCRを試してみたところ、傾いている文字を全く認識しなかったので、
OCRをかける前に文字の傾きを水平補正しようと思ったのだった。

■ 事前作業
NuGetパッケージマネージャーでOpenCvSharp4.Windowsをインストールします。
OpenCvSharp.Extensionsも同時にインストールされます。

■ 留意点
読み取る画像に直線が含まれている必要があります。

C#
using OpenCvSharp;
using OpenCvSharp.Extensions;

private Bitmap tiltPicture(Bitmap img_src)
{
    // 画像の読み込み
    //Mat img = Cv2.ImRead(@"c:\temp\naname.png");
    Mat img = BitmapConverter.ToMat(img_src);

    // モノクロ・グレースケール画像へ変換
    Mat imgGray = new Mat();
    Cv2.CvtColor(img, imgGray, ColorConversionCodes.BGR2GRAY);

    // エッジ検出
    Mat edges = new Mat();
    Cv2.Canny(imgGray, edges, 50, 150, 3);

    // 直線検出
    LineSegmentPoint[] lines = Cv2.HoughLinesP(edges, 1, Math.PI / 180, 100, 100, 10);

    // 直線の角度を計算
    double[] angles = new double[lines.Length];
    for (int i = 0; i < lines.Length; i++)
    {
        LineSegmentPoint line = lines[i];
        double medianAngle = Math.Atan2(line.P2.Y - line.P1.Y, line.P2.X - line.P1.X) * 180.0 / Math.PI;
        angles[i] = medianAngle;
    }

    // 直線の角度の中央値を計算
    double angle = CalculateMedian(angles);

    // アフィン変換を適用してスキュー角度を修正
    (int h, int w) = (img.Rows, img.Cols);
    Point2f center = new Point2f(w / 2f, h / 2f);
    Mat rotationMatrix = Cv2.GetRotationMatrix2D(center, angle, 1.0);
    Mat rotateImg = new Mat();
    Cv2.WarpAffine(img, rotateImg, rotationMatrix, new OpenCvSharp.Size(w, h), flags: InterpolationFlags.Cubic, borderMode: BorderTypes.Replicate);

    Bitmap bitmap = BitmapConverter.ToBitmap(rotateImg);

    // プログラムを終了する前に解放する
    img.Release();
    imgGray.Release();
    edges.Release();
    rotateImg.Release();

    return bitmap;
}

/// <summary>
/// 直線の角度の中央値を計算
/// </summary>
/// <param name="values"></param>
/// <returns>中央値</returns>
static double CalculateMedian(double[] values)
{
    Array.Sort(values);
    int length = values.Length;

    if (length % 2 == 0)
    {
        // 偶数の場合は中央の2つの値の平均を取る
        int midIndex1 = length / 2 - 1;
        int midIndex2 = length / 2;
        return (values[midIndex1] + values[midIndex2]) / 2.0;
    }
    else
    {
        // 奇数の場合は中央の値をそのまま返す
        int midIndex = length / 2;
        return values[midIndex];
    }
}

1
0
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0