web-dev-qa-db-ja.com

C#で画像をx度回転させる方法は?

私はいくつかの検索を行いましたが、私がやりたいことを実行している関数を見つけることができません。

スキャンしたドキュメントの画像ファイルとテキストがありますが、ドキュメントがある程度回転しているので、テキストが互いにインラインになるように回転させたいと思います。

完璧な世界では、これを自動的に行う機能である必要がありますが、何も見つからず、自動的に機能させるために理解していることは、画像を分析する必要があり、大きなことだと思います。

しかし、ウェブサイト上の画像を手動で回転させるツールを実行しましたが、回転を画像ファイルに保存する機能が必要になりました。

これはいくつかの異なる方法のようですが、私が望むことを実行してテストした人は誰もいません。

私が見つけた関数は、私が望んでいるように機能します。

public static Bitmap RotateImg(Bitmap bmp, float angle, Color bkColor) {
int w = bmp.Width;
int h = bmp.Height;
PixelFormat pf = default(PixelFormat);
if (bkColor == Color.Transparent)
{
    pf = PixelFormat.Format32bppArgb;
}
else
{
    pf = bmp.PixelFormat;
}

Bitmap tempImg = new Bitmap(w, h, pf);
Graphics g = Graphics.FromImage(tempImg);
g.Clear(bkColor);
g.DrawImageUnscaled(bmp, 1, 1);
g.Dispose();

GraphicsPath path = new GraphicsPath();
path.AddRectangle(new RectangleF(0f, 0f, w, h));
Matrix mtrx = new Matrix();
//Using System.Drawing.Drawing2D.Matrix class 
mtrx.Rotate(angle);
RectangleF rct = path.GetBounds(mtrx);
Bitmap newImg = new Bitmap(Convert.ToInt32(rct.Width), Convert.ToInt32(rct.Height), pf);
g = Graphics.FromImage(newImg);
g.Clear(bkColor);
g.TranslateTransform(-rct.X, -rct.Y);
g.RotateTransform(angle);
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImageUnscaled(tempImg, 0, 0);
g.Dispose();
tempImg.Dispose();
return newImg; }

ただし、これによって画像ファイルの高さと幅が変更されることはないため、画像ファイルは同じサイズになりますが、画像の「オブジェクト」は拡大縮小および回転されます。

どうすればこれをうまくやれるか考えていますか?

Answer解像度が300の画像で機能するソリューションを 古い回答はこちら で見つけました。

12
RickardP

私があなたの質問を正しく理解した場合、あなたは基本的に、回転した画像の新しいサイズと、回転した画像を新しいビットマップに配置する方法を考えたいと思うでしょう。

enter image description here

この図は、解決策を明確にするのに役立つことを願っています。これが少しの擬似コードです:

sinVal = abs(sin(angle))
cosVal = abs(cos(angle))
newImgWidth = sinVal * oldImgHeight + cosVal * oldImgWidth
newImgHeight = sinVal * oldImgWidth + cosVal * oldImgHeight
originX = 0
originY = sinVal * oldImgWidth

NewImgWidthとnewImgHeightから新しいイメージを作成し、原点(originX、originY)を中心に回転を実行してから、このポイントまでイメージをレンダリングします。角度(度単位)が-90度から0度(図に示されている)の間にない場合、これは倒れます。 0度から90度の間の場合は、原点を変更するだけです。

originX = sinVal * oldImgHeight
originY = 0

90度から270度(-90度)の範囲にある場合は、少し注意が必要です(以下のサンプルコードを参照)。

あなたのコードは書き直されました(簡単にテストされました)-それは少し危険ですが、うまくいくようです:

public static Bitmap RotateImg(Bitmap bmp, float angle, Color bkColor)
{
    angle = angle % 360;
    if (angle > 180)
        angle -= 360;

    System.Drawing.Imaging.PixelFormat pf = default(System.Drawing.Imaging.PixelFormat);
    if (bkColor == Color.Transparent)
    {
        pf = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
    }
    else
    {
        pf = bmp.PixelFormat;
    }

    float sin = (float)Math.Abs(Math.Sin(angle * Math.PI / 180.0)); // this function takes radians
    float cos = (float)Math.Abs(Math.Cos(angle * Math.PI / 180.0)); // this one too
    float newImgWidth = sin * bmp.Height + cos * bmp.Width;
    float newImgHeight = sin * bmp.Width + cos * bmp.Height;
    float originX = 0f;
    float originY = 0f;

    if (angle > 0)
    {
        if (angle <= 90)
            originX = sin * bmp.Height;
        else
        {
            originX = newImgWidth;
            originY = newImgHeight - sin * bmp.Width;
        }
    }
    else
    {
        if (angle >= -90)
        originY = sin * bmp.Width;
        else
        {
            originX = newImgWidth - sin * bmp.Height;
            originY = newImgHeight;
        }
    }

    Bitmap newImg = new Bitmap((int)newImgWidth, (int)newImgHeight, pf);
    Graphics g = Graphics.FromImage(newImg);
    g.Clear(bkColor);
    g.TranslateTransform(originX, originY); // offset the Origin to our calculated values
    g.RotateTransform(angle); // set up rotate
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
    g.DrawImageUnscaled(bmp, 0, 0); // draw the image at 0, 0
    g.Dispose();

    return newImg;
}

三角関数の度からラジアンへの変換(180度== Piラジアン)に注意してください

編集:大きな問題は負のsin値であり、Origin x/yを計算するときに幅/高さが混乱しました-これは今はうまくいくはずです(テスト済み)

編集:任意の角度を処理するようにコードを変更

32
VisualMelon

これは厳密には上記のVisualMelonによるNiceの回答に対するコメントですが、コメントを追加することは許可されていません...

コードには2つの小さなバグがあります

a)モジュラス後の最初のチェックは、2つに分割するか、たとえば次のように変更する必要があります。

if (180<Math.Abs(angle)) angle -= 360*Math.Sign(angle);

そうしないと、+ 180〜 + 360しか処理されなかったため、-360〜-180の角度は失敗します

b)newImg割り当ての直後に、解決割り当てがありません。

newImg.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);

省略した場合、ソースが96 dpiでない場合、画像は拡大縮小されます。

....そして、スティックを分割する場合、寸法とオフセットの中間計算は2倍に保ち、最後にフロートするように減らす必要があります。

1
Eske Rahn