■ 自作で任意角度で画像を回転する
実装方法【1】画像の中心を原点に平行移動 【2】指定した角度で回転 【3】描画領域の中心に表示するように平行移動* 画像を回転する場合、原点(0, 0)ではなく画像の中心周りに回転させる
=> [1] 画像の中心を原点(0, 0)にするため、 入力画像の幅 width/2 と 高さ height/2 を引く => [2] 変換後の座標x', y'は、出力画像の中心にするため、 出力画像の幅 width'/2と高さ height'/2 を加えるhttps://algorithm.joho.info/image-processing/affine-transformation-rotation/
* 画像に穴があくので、その対策をする
=> 理由は、転送元から転送先にピクセルを移動した際に、 小数の座標があるとそこで穴が開いてしまう => 以下のサイトが図があって分かりやすいhttp://yaju3d.hatenablog.jp/entry/2013/07/14/133031
【解決案】 * 転送先から転送元にピクセルを埋める
■ サンプル
using System; using System.Drawing; using System.Windows.Forms; namespace SampleForm { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { try { string imagePath = this.textBox1.Text; double angle = double.Parse(this.textBox2.Text); int pictureBoxWidth = this.pictureBox1.Width; int pictureBoxHeight = this.pictureBox1.Height; // 描画領域の中心 int centerPictureBoxX = pictureBoxWidth / 2; int centerPictureBoxY = pictureBoxHeight / 2; // 2倍の描画領域サイズの空の描画領域を生成 Bitmap bitmap = new Bitmap(pictureBoxWidth * 2, pictureBoxHeight * 2); Bitmap sourceBitmap = new Bitmap(imagePath); // 画像サイズ int imageWidth = sourceBitmap.Width; int imageHeight = sourceBitmap.Height; // 画像の中心 int centerImageX = imageWidth / 2; int centerImageY = imageHeight / 2; double theta = angle * (Math.PI / 180); double cos = Math.Cos(theta); double sin = Math.Sin(theta); for (int x = 0; x < bitmap.Width; x++) { for (int y = 0; y < bitmap.Height; y++) { int dx = (int)(((x - centerImageX) * cos) - ((y - centerImageY) * sin) + centerImageX - centerPictureBoxX); int dy = (int)(((x - centerImageX) * sin) + ((y - centerImageY) * cos) + centerImageY - centerPictureBoxY); if ((0 < dx && dx < imageWidth) && (0 < dy && dy < imageHeight)) { Color color = sourceBitmap.GetPixel(dx, dy); bitmap.SetPixel(x, y, color); } } } if (this.pictureBox1.Image != null) { this.pictureBox1.Image.Dispose(); } this.pictureBox1.Image = bitmap; } catch (Exception ex) { this.label1.Text = ex.Message; } } } }
■ 補足:回転の数式的意味
https://mathwords.net/heimenkaitenhttps://algorithm.joho.info/image-processing/affine-transformation-rotation/
が分かりやすい
行列の掛け算
* ベクトル a に行列を掛けると、以下のようになる | -1 0 || 2 | | (-1)*2 + 0*3 | | -2 | | || | = | | = | | | 0 1 || 3 | | 0*2 + 1*3 | | 3 | ^^^^^^ ^^^ ~^^^ 行列 a b => 行列を掛けることにより、ベクトル a の終点位置が変わる => 行列を掛けることにより、ベクトル a が ベクトル b に変換される
原点から元にした回転を求める
| x' | | cosθ -sinθ || x | | | = | || | | y' | | sinθ cosθ || y | x' = x cosθ - y sinθ y' = x sinθ + y cosθ【パラメータ説明】
x , y : 回転前の座標 x', y' : 回転後の座標 | cosθ -sinθ | | | : 回転行列 | sinθ cosθ |
回転座標から元の座標を求める(逆変換)
| x | | cosθ sinθ || x' | | | = | || | | y | | -sinθ cosθ || y' | x = x' cosθ + y' sinθ y = - x' sinθ + y' cosθ【計算で導く】
| x' | | cosθ -sinθ || x | | | = | || | | y' | | sinθ cosθ || y | ^^^^ ^^^^^^^^^^^^^^ ^^^ X = A x とし、「A-1をAの逆行列」とすると... A-1 X = A-1 A x => A-1 X = E x (A-1 A = E(E:単位行列。数字でいうと「1」)) => x = A-1 X ... (※1) | a b | P = | | の逆行列 P-1 は... | c d | 1 | d -b | P-1 = --------| | ad - bc | -c a | である。 なので、A-1 は、Aの逆行列なので... 1 | cosθ -sinθ | A-1 = ----------------------------- | | cosθ* cosθ - sinθ* sinθ | sinθ cosθ | | cosθ sinθ | => A-1 = | | (分母の「sin^2θ+cos^2θ=1」... ※2) | -sinθ cosθ | よって、※1は、 | x | | cosθ sinθ || x' | | | = | || | | y | | -sinθ cosθ || y' | ^^^^ ^^^^^^^^^^^^^^ ~^^^ x = A-1 Xhttp://www4.airnet.ne.jp/tmt/mathself/matrix8.pdf
※2 : 分母の「sin^2θ+cos^2θ=1」について
https://juken-mikata.net/how-to/mathematics/sin2-cos2-1.html