Windowsアプリで画像を45度回転させるプログラムの作り方

このQ&Aのポイント
  • Windowsアプリケーションでボタン一つで画像を45度回転させるプログラムを作りたいです。
  • 回転変化後の画像には線形補間法(バイリニア法)を使っての補間処理をして表示させたいです。
  • 言語はC#を使用しています。
回答を見る
  • ベストアンサー

幾何学変換

Windowsアプリケーションでボタン一つで画像を45度回転させるプログラムを作りたいのですが、プログラムがよく分かりません。どなたか教えていただけないでしょうか。あと、回転変化後の画像には線形補間法(バイリニア法)を使っての補間処理をして表示させたいのですがよろしいでしょうか。言語はC#です。 自分で色々プログラム組んでるのですがなかなか出来ないです。今組んでるプログラムを実行すると変な風に実行されます。 組んでる途中のプログラム↓↓ private Color[,] SpinImage(Color[,] colImage) { int iHeight = colImage.GetLength(0); int iWidth = colImage.GetLength(1); //Console.WriteLine(iHeight + "\t" + iWidth); double X = 45.0 * Math.PI / 180.0; int iHeight2 = (int)(iWidth * Math.Cos(X) + iHeight * Math.Sin(X)); int iWidth2 = (int)(iWidth * Math.Sin(X) + iHeight * Math.Cos(X)); Color[,] colImage2 = new Color[iHeight2, iWidth2]; //Console.WriteLine(iHeight +"\t" +iWidth); for (int j = 0; j < iHeight-1; j++) { for (int i = 0; i < iWidth-1; i++) { int m = (int)(i * Math.Cos(X) + j * Math.Sin(X)); int n = (int)(-1 * i * Math.Sin(X) + j * Math.Cos(X)) + 100; int iRed = 0; int iGreen = 0; int iBlue = 0; // Console.WriteLine("y:"+ j +"\t i:"+i+"\t m:" +m + "\tn:" + n); iRed = colImage[j, i].R; iGreen = colImage[j, i].G; iBlue = colImage[j, i].B; #region a //if ((j >= 0) && (j < iWidth) && (i >= 0) && (i < iHeight)) //{ // iRed = colImage[i, j].R; // iGreen = colImage[i, j].G; // iBlue = colImage[i, j].B; // colImage[m, n] = Color.FromArgb(iRed, iGreen, iBlue); //} //else //{ // colImage[m, n] = Color.FromArgb(0, 0, 0); //} #endregion if (m < iHeight && n >= 0) { colImage2[m, n] = Color.FromArgb(iRed, iGreen, iBlue); } } } return colImage2; }

質問者が選んだベストアンサー

  • ベストアンサー
  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.1

「変な風に実行されます」というのだけでは、どういう状況下わからないのですが、 それは、所々に黒いポツポツが出るとか、そういった状況ですか? そうだとしたら、この手のプログラムでの典型的なミスですね。 質問者さんのコードは、 「変換元の各画素」について、「変換後の座標」を算出して、「その座標位置に書き込む」 この方法では「変換後の画像の全ての画素に書き込まれるとはかぎりません」ので、結果として、所々書き込まれずに黒いままの画素が出てきたりします。 ループを逆にして、 「変換先の各画素」についてループ処理して、 「変換前の座標」を算出(変換が45度回転なら、-45度回転した座標を求める)して、 「その座標の画素を取ってきて書き込む」 ようにすれば、そういった歯抜けはなくなります。 動作確認はしてませんが、質問者さんのコードは修正すると --- int iOffsetX1 = iWidth/2 int iOffsetY1 = iHeight/2; int iOffsetX2 = iWidth2/2 int iOffsetY2 = iHeight2/2; for (int m = 0; m < iHeight2; m++) { for (int n = 0; n < iWidth2; n++) { int i = (int)((n-iOffsetX2) * Math.Cos(X) - (m-iOffsetY2) * Math.Sin(X)) + iOffsetX1; int j = (int)((n-iOffsetX2) * Math.Sin(X) + (m-iOffsetY2) * Math.Cos(X)) + iOffsetY1; int iRed = colImage[j, i].R; int iGreen = colImage[j, i].G; int iBlue = colImage[j, i].B; if (i >= 0 && i < iWidth && j > 0 && j < iHeight) { colImage2[m, n] = Color.FromArgb(iRed, iGreen, iBlue); } } } --- こんな感じになるかと。

shamal1988
質問者

補足

回答ありがとうございます。 >>「変な風に実行されます」というのだけでは、どういう状況下わからないのですが、それは、所々に黒いポツポツが出るとか、そういった状況ですか? >>そうだとしたら、この手のプログラムでの典型的なミスですね。 これ何ですけど、実行すると画像に白い点が入り、画像の一部が欠けてしまいます。 それと自分の書いたプログラムのfor文から回答者様のプログラムを書き換えて実行してみたのですが、iRedの所で「インデックスが配列の境界外です」って表示されプログラムが停止しました。

その他の回答 (2)

  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.3

> 回転した画像がPictureボックス内にキッチリと入らずハミ出て回転した画像が欠けてしまいます。 はみ出るのが1~2ドットだとしたら、 --- int iHeight2 = (int)(iWidth * Math.Cos(X) + iHeight * Math.Sin(X)); int iWidth2 = (int)(iWidth * Math.Sin(X) + iHeight * Math.Cos(X)); --- ここで、整数にしている(切り捨てている)から、結果の画像サイズが小さくなってしまっているからでしょう。切り上げる必要があります。

  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.2

> iRedの所で「インデックスが配列の境界外です」って表示されプログラムが停止しました。 すみません、ifの配置を間違えました。 iRed~iBlueの代入はifの中で、 --- if (i >= 0 && i < iWidth && j > 0 && j < iHeight) { int iRed = colImage[j, i].R; int iGreen = colImage[j, i].G; int iBlue = colImage[j, i].B; colImage2[m, n] = Color.FromArgb(iRed, iGreen, iBlue); } --- といった感じになります。 …ていうか、これは、 --- if (i >= 0 && i < iWidth && j > 0 && j < iHeight) { colImage2[m, n] = colImage[j, i]; } --- で十分ですね。

shamal1988
質問者

補足

ありがとうございます。 何度も申し訳ありません。 実行できたのですが、回転した画像がPictureボックス内にキッチリと入らずハミ出て回転した画像が欠けてしまいます。どうすればよろしいでしょうか。

関連するQ&A

  • C#で独自の型を定義したい

    C# 2010 version 4.0を使用しています。 次のようなint型とstring型を混合したMixedという型を定義したいのですが、 public class Mixed { private int _TheInt = 0; public int TheInt { get { return _TheInt; } set { _TheInt = value; } } private string _TheString = null; public string TheString { get { return _TheString; } set { _TheString = value; } } public static implicit operator int(Mixed m) { return m.TheInt; } public static implicit operator Mixed(int x) { Mixed m = new Mixed(); m.TheInt = x; return m; } public static implicit operator string(Mixed m) { return m.TheString; } public static implicit operator Mixed(string x) { Mixed m = new Mixed(); m.TheString = x; return m; } } //Mixed m = 1; //Console.WriteLine(m); //これだとコンパイルエラーになる //Console.WriteLine(m.TheInt.ToString()); //「1」と表示される int i = 1; Console.WriteLine(i); //「1」と表示される object o = 1; Console.WriteLine(o); //「1」と表示される Console.WriteLineなどで表示する場合に 例えばint型なら int i = 1; Console.WriteLine(i); //「1」と表示される という具合にそのまま「1」と表示されます。 でもこのMixedの場合、 Mixed m = 1; Console.WriteLine(m.TheInt.ToString()); //「1」と表示される これでは「1」と表示されるのですが、 Console.WriteLine(m); //これだとコンパイルエラーになる このやり方だとエラーになります。 なんとかしてint型やobject型のようにToString()を使わずに 表示させることはできないでしょうか?

  • 先に計算したほうがいいのでしょうか?

    下記のプログラムを作ったのですが、 Math.PI / 180 の部分は先に計算しておいたほうが処理が 早くなると言われたのですがそうなのでしょうか? 先に掛け算をしないといけないような気がするのですが。 import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import java.lang.Math; public class Test9 { public static void main(String[] args) { int r = (args.length > 0)? Integer.parseInt(args[0]):100; int n = (args.length > 1)? Integer.parseInt(args[1]):16; int x, y, x1, y1; try { BufferedImage image=new BufferedImage(r*2+10,r*2+10,BufferedImage.TYPE_INT_RGB); Graphics2D g2d=image.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0,0,r*2+10,r*2+10); g2d.setColor(Color.BLACK); for ( double i = 0.0; i < 360.0; i += 360.0 / n ) { x1 = (int) ( r * Math.cos( i * Math.PI / 180 ) ); y1 = (int) ( r * Math.sin( i * Math.PI / 180 ) ); for( double j = i + 360 / n; j < 360.0; j += 360.0 / n ) { x = (int) ( r * Math.cos( j * Math.PI / 180 ) ); y = (int) ( r * Math.sin( j * Math.PI / 180 ) ); g2d.drawLine( x1 + r + 5, y1 * (-1) + r + 5, x + r + 5, y * (-1) + r + 5 ); } } ImageIO.write(image, "JPEG", new File("c:\\test9.jpg")); } catch(Exception e) { e.printStackTrace(); } } }

    • ベストアンサー
    • Java
  • 超幾何分布

    超幾何分布と二項定理について教えてください (1+x)^(m+n) =(1+x)^m*(1+x)^n =Σ{i=0~m}mCi*x^i*Σ{j=0~n}nCj*x^j =Σ{i=0~m}*Σ{j=0~n}nCj*mCi*nCj*x^(i+j)   (1+x)^(m+n)=Σ{m=0~m+n}(m+n)Cm*x^m に持って行きたいのですが、問題の3つ目の=から先が理解できません。 シグマ二つをどう処理すればよいのでしょうか? アドバイスいただけませんか。 よろしくお願いいたします

  • jpeg の DCT 変換がうまくいきません。

    jpeg の DCT 変換がうまくいきません。 jpegを作りたいのですが、DCT変換で詰まってしまいました。 よろしければ、ご教授いただけませんでしょうか? http://www.ann.hi-ho.ne.jp/jiro/assyuku2.htm のDCT変換の例を見て、プログラムを作ってみましたが、 変換例と同じ値に変換されませんでした。 作ってみたプログラムです。 #include <iostream> #include <stdio.h> #define _USE_MATH_DEFINES #include <math.h> using namespace std; void main(int argc, char** argv) {   double cu = 1.0;   double cv = 1.0;     double long sum = 0.0;      int before[8][8] ={     {57,49,44,39,33,28,23,22},     {55,45,39,33,28,21,21,19},     {55,44,37,31,20,17,17,16},     {58,44,37,29,17,15,16,14},     {66,46,31,22,16,14,12,10},     {71,49,32,24,14,12,21,19},     {69,50,30,22,12,4,-1,-2},     {62,42,25,17,7,1,-16,-16}      };   int after[8][8];   ZeroMemory(after,sizeof(int)*64);   for(int v =0; v < 8; v++)   {     if(v) cv =1.0;     if(!v) cv = 1 / sqrt(2.0);     for(int u = 0; u < 8; u++)     {       if(u) cu =1.0;       if(!u) cu = 1 / sqrt(2.0);       sum = 0.0;       for(int y = 0; y < 8; y++)       {         for(int x = 0; x < 8; x++)         {           double long a = cos((2 * x + 1)*u*M_PI/16) * cos(( 2 * y + 1)*v*M_PI/16);           double long d = before[x][y];           sum += d * a;         }       }       after[u][v] = int(cu*cv*sum/4);     }   }   for(int j = 0; j < 8; j++)   {     for(int i = 0; i < 8; i++)     {       cout << after[i][j] << ",";     }     cout << endl;   }   int b;   cin >> b ; }

  • 「関数ポインタとして評価されない式」?(sin波)

    知っている(ある程度のプログラミング知識がある)方にはばかばかしい内容かもしれませんが・・・。 sin波とcos波を描くものをC++でプログラミングしている途中です。 いざ実行!と思ってコンパイルすると、「関数ポインタとして評価されない式を使って、関数を呼び出そうとしました。」と出たのですが、何が原因かわかりません。 プログラム内容↓ #include <stdio.h> #include <math.h> #define PAI 3.14 (中略・ペン設定など) double j=0.0, sinwave[400]; int x0=50, y0=50, i=0; while(i<400){   for(j=0.0; j<=PAI; j=j+0.1){ sinwave[i]=sin(j); ●ここが原因らしい   }   i++; } (後略・ペンdeleteなど) math.hもあるし、特に変なところはないと思います。 また、そのあとにMoveTo,LineToでsin波を描きたいのですが、 for文で繰り返し MoveTo(x0+sin[i],?) LineTo(x0+sin[i+1],?) の、はてなの部分に何を入れればよいかわかりません。(x0:勝手にきめた原点)x0+sin[i]も、これで良いのか「?」です・・・。 お願いします。

  • 離散コサイン変換について

    Webにあるサンプルソースや書籍のソースを見て、 感じの違うソースなのですが、 以下のソースはDCTになっているのでしょうか? 書籍には、 Data[N]=1.2.3[.3.2.1<-付け足] して、2NでDFTすればよいとありました。 ですが、式を見てもそういう記述にはなっていません。 よろしくお願いします。 #include<stdio.h> #define _USE_MATH_DEFINES #include<math.h> int main() { double *Data; double *DataAfter; double *DataConvert; int DataLen; DataLen=16; Data=new double[DataLen*2]; DataAfter=new double[DataLen*2]; DataConvert=new double[DataLen*2]; for(int i=0;i<DataLen;i++) { Data[i]=1.5*i; } for(int i=0;i<DataLen;i++) { Data[DataLen+i]=Data[DataLen-1-i]; } for(int i=0;i<DataLen*2;i++) { DataAfter[i]=0; for(int j=0;j<DataLen*2;j++) { DataAfter[i]+=1.0*Data[i]*cos(M_PI*i*j/(DataLen*2)); } DataAfter[i]/=DataLen; } for(int i=0;i<DataLen*2;i++) { DataConvert[i]=0; for(int j=0;j<DataLen*2;j++) { DataConvert[i]+=1.0*DataAfter[i]*cos(-M_PI*i*j/(DataLen*2)); } DataConvert[i]*=DataLen; } for(int i=0;i<DataLen*2;i++) { printf("%d %f\t%d %f\t%d %f\n",i,Data[i],i,DataAfter[i],i,DataConvert[i]); } delete[] Data; delete[] DataAfter; delete[] DataConvert; getchar(); return 0; }

  • C#について

    using System; class AddressBook: PhoneBook { private string address; public AddressBook() : base() { address=""; } public void Input() { base.Input(); Console.Write("住所?"); string address=Console.ReadLine(); } public void Writes() { base.Write(); Console.WriteLine("住所:{0}",address); } } class kadai62 { public static void Main() { Console.Write("電話帳に入力する人数を入れてください:"); int n = int.Parse(Console.ReadLine()); AddressBook[] pb = new AddressBook[n]; int i; for (i = 0; i < pb.Length; i++) { pb[i] = new AddressBook(); Console.WriteLine("{0}番目を入力してください.", i+1); pb[i].Input(); } Console.WriteLine(); while(true) { Console.Write("どこから探しますか?[1:氏名,2:自宅電話,3:携帯電話,0:終了] "); int t = int.Parse(Console.ReadLine()); if (t == 0) break; Console.Write("探す文字列は?"); string s = Console.ReadLine(); for (i = 0; i < pb.Length; i++) { if (pb[i].Search(t, s)) pb[i].Writes(); } } } } このプログラムで public void Writes() { base.Write(); Console.WriteLine("住所:{0}",address); } のaddressが出力されません・・どうすれば 出力されますか?

  • [C#]順序通りに実行されない

    次のC#のプログラムについて質問です。 //ここから using System; class Myclass { double[] point; string[] name; int nMax; public double this[string str] { get { for (int i = 0; i < nMax; i++) { if (str == name[i]) return point[i]; } return -1.0; } set { for (int i = 0; i < nMax; i++) { if (str == name[i]) { point[i] = value; break; } } } } public Myclass(int n) { point = new double[n]; name = new string[n]; nMax = n; setname(); } void setname() { for(int i=0;i<nMax;i++) { Console.Write("生徒名[{0}] = ",i); name[i] = Console.ReadLine(); } } } class ch08ex02 { public static void Main() { Console.WriteLine( "まずはじめに生徒数と、全員の名前入力が必要です"); Console.Write("生徒数--- "); string strn = Console.ReadLine(); Myclass mc = new Myclass(int.Parse(strn)); string ans, strp; Console.WriteLine("X入力で終了"); while (true) { Console.Write("点数を入力したい生徒名--- "); if ((ans = Console.ReadLine()) == "X") break; Console.Write("点数--- "); strp = Console.ReadLine(); mc[ans] = double.Parse(strp); } Console.WriteLine(); Console.WriteLine("X入力で終了"); while (true) { Console.Write("点数を知りたい生徒名--- "); ans = Console.ReadLine(); if (ans == "X") break; Console.WriteLine("{0}くんの点数は{1}点です", ans, mc[ans]); } } } //ここまで これはプロパティを使って生徒の点数を配列に格納したり参照したりするプログラムですが、実行するとMain()の    Console.Write("生徒数--- "); string strn = Console.ReadLine(); Myclass mc = new Myclass(int.Parse(strn)); string ans, strp; Console.WriteLine("X入力で終了");    while (true) { Console.Write("点数を入力したい生徒名--- "); if ((ans = Console.ReadLine()) == "X") break; Console.Write("点数--- "); strp = Console.ReadLine(); mc[ans] = double.Parse(strp); } の部分が思い通りに動いてくれません。 まずコンソールに「生徒数--- 」と表示されConsole.ReadLine()で数字を受け取り、その数からMyclassのインスタンスを作成し、「X入力で終了」と表示した後whileループで生徒名と点数の入力を受け付けるはずです。 しかしこれを実行すると「X入力で終了」と表示する前にwhileループに入り、ループを抜けた後「X入力で終了」と表示されます。 つまり順番が逆になっているわけです。 なぜこうなるかわかりません。 誰か教えてください、お願いします。

  • {}が・・・・。

    int i=1; if(i==0) { System.Console.WriteLine("0です。"); } else { System.Console.WriteLine("0ではありません"); } と勝手にこうなってしまいます。 個人的に if(i==0){ System.Console.WriteLine("0です。"); }else{ System.Console.WriteLine("0ではありません"); } としたいのですが、1つ1つ強引に変えていくしかないのでしょうか?

  • C#(VS2013)の漢字<-->数値変換について

    C#(VS2013)の漢字、数値変換に関する質問です。 下記のコードは、文字を数値、数値を文字に変換するものです。 この場合、文字が"A"の場合は、相互に変換できますが、 文字が "換" の場合は、変換出来ません。 Q1)この件に関しまして、回答、コメント頂けますと大変有難いです。 //Project: c:\wk_VS2013ACs\TT_CharCLR.sln using System; using System.Text; public static class Program { public static void Main() { Char c; Int32 n; string str = "シフトJISへ変換"; Encoding sjisEnc = Encoding.GetEncoding("Shift_JIS"); byte[] bytes = sjisEnc.GetBytes(str); Console.WriteLine(BitConverter.ToString(bytes)); // 出力:83-56-83-74-83-67-4A-49-53-82-D6-95-CF-8A-B7 // Convert number <-> character using C# casting // c = (Char)0x95CF; 変 // c = (Char)0x8AB7; 換 //c = (Char)65; c = (Char)0x8AB7; //<------旨く行かず Console.WriteLine(c); // Displays "A" n = (Int32)c; Console.WriteLine(n); // Displays "65" c = unchecked((Char)(65536 + 65)); //============= Console.WriteLine(c); // Displays "A" // Convert number <-> character using Convert c = Convert.ToChar(65); Console.WriteLine(c); // Displays "A" n = Convert.ToInt32(c); Console.WriteLine(n); // Displays "65" // This demonstrates Convert's range checking try { c = Convert.ToChar(70000); // Too big for 16 bits // c = Convert.ToChar(0x95CF); // Too big for 16 bits Console.WriteLine(c); // Doesn't execute } catch(OverflowException) { Console.WriteLine("Can't convert 70000 to a Char."); } // Convert number <-> character using IConvertible c = ((IConvertible)65).ToChar(null); Console.WriteLine(c); // Displays "A" n = ((IConvertible)c).ToInt32(null); Console.WriteLine(n); // Displays "65" Console.ReadKey(); }//public static void Main() { }//namespace

専門家に質問してみよう