看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《sbty173 (我愛單眼皮)》之銘言: : 各位前輩好 : 我最近用在做圖片的旋轉 用的是2003.net 的mfc寫 : 可是沒有什麼頭緒 : 去程式討論區發現有前輩做了一個類似的程式 : http://delphi.ktop.com.tw/topic.asp?topic_id=34152 : 他的button7 是作旋轉的 : 但他是用BCB做 : 內容如下 : void __fastcall TForm1::Button7Click(TObject *Sender) : { : Image2->AutoSize=true; : int angle=StrToInt(Edit2->Text); : Graphics::TBitmap *Bitmap1=new Graphics::TBitmap; : Graphics::TBitmap *Bitmap2=new Graphics::TBitmap; : Bitmap1->Assign(Image1->Picture->Bitmap); : float radians=(2*3.1416*angle)/360; //不懂為何這樣設定?? : float cosine=(float)cos(radians); : float sine=(float)sin(radians); : float Point1x=(-Bitmap1->Height*sine); : float Point1y=(Bitmap1->Height*cosine); : float Point2x=(Bitmap1->Width*cosine-Bitmap1->Height*sine); : float Point2y=(Bitmap1->Height*cosine+Bitmap1->Width*sine); : float Point3x=(Bitmap1->Width*cosine); : float Point3y=(Bitmap1->Width*sine); : float minx=min(0,min(Point1x,min(Point2x,Point3x))); : float miny=min(0,min(Point1y,min(Point2y,Point3y))); : float maxx=max(Point1x,max(Point2x,Point3x)); : float maxy=max(Point1y,max(Point2y,Point3y)); : int Bitmap2Width=(int)ceil(maxx-minx); : int Bitmap2Height=(int)ceil(maxy-miny); : Bitmap2->Height=Bitmap2Height; : Bitmap2->Width=Bitmap2Width; : for(int x=0;x<Bitmap2Width;x++) : { : for(int y=0;y<Bitmap2Height;y++) : { : int Bitmap1x=(int)((x+minx)*cosine+(y+miny)*sine); : int Bitmap1y=(int)((y+miny)*cosine-(x+minx)*sine); : if(Bitmap1x>=0&&Bitmap1x<Bitmap1->Width&&Bitmap1y>=0&&Bitmap1y<Bitmap1->Height) : { : Bitmap2->Canvas->Pixels[x][y]=Bitmap1->Canvas->Pixels[Bitmap1x][Bitmap1y]; : } : } : } : 我看不太懂為什麼要這樣做運算 : 不曉得前輩們可以幫我解釋一下嗎 : 還有請問語法有些我不太懂 要怎麼轉過來呢? 其實圖片的旋轉很簡單,用到的都是數學的公式而已!:) 把一張圖想成是放在一個數學座標上的圖來想,應該會比較好理解。 在數學的旋轉上面.... 如果我要將 (x, y) 的點,逆時鐘旋轉 30 度變成 (x' , y') 哪麼公式是... x' = cos30 * x - sin30 * y y' = sin30 * x + cos30 * y 看了上面的公式,直覺的做法就是用雙 for 將原圖全部的 pixel 跑一次... 然後算出旋轉後的新位置,存到新的陣列中。 但是這樣做你會發現.... for (i = 0; i < old_bmp_h; i++) for (j = 0; j < old_bmp_w; j++) { x' = cos(30度) * j - sin(30度) * i; y' = cos(30度) * j - sin(30度) * i; // cos() 裡面要傳入的是 "徑度" // radians=(2*3.1416*angle)/360; //不懂為何這樣設定?? // 就是在轉成徑度用的。 new_bmp[x'][y'] = old_bmp[i][j]; } 如此作完,結果一定很難看.... 因為 cos(30) * j - sin(30) * i 算出來的結果有可能是小數 如果說 x' = 30.1 那會被強制變成整數為 30 下一個 pixel 算出來 x' = 30.3 結果還是會是 30 所以,到最後的結果就會有很多旋轉前的 pixel 都填入同一個旋轉後的 pixel 而很多旋轉後的 pixel 沒有值填入。 因此!我們需要逆向思考! for (i = 0; i < new_bmp_h; i++) for (j = 0; j < new_bmp_w; j++) { x = ............... ; y = ............... ; // 倒過來個公式自己用數學代數算一下就出來了!:) new_bmp[i][j] = old_bmp[x][y]; } 這樣是把旋轉後的每一個 pixel 都跑過一次.... 反算應該要用旋轉前的哪一個 pixel 填入。 做到這樣,結果就會好很多了,但是會發現會有鋸齒狀。 所以有更好的演算法可以幫助你做的更好。 1. 最近鄰居法。 最簡單,但是效果非常有限。 2. biliner。 簡單,效過又好。 3. bicubic。 複雜,效過最好。 建議可以做到2就可以了!^^ 至於詳細的內容,可以用 google 找一下就可以找到了。^^ 2, 3 也是常常用在將圖片放大、縮小的時候。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 218.163.159.53
qazq:反算的公式,發現原PO所貼的code裡面就有了 :) 09/27 21:59
※ 編輯: qazq 來自: 218.163.159.53 (09/27 22:02)
moonls:寫的很清楚 !! 獲益良多 09/28 00:23
renderer:推 09/28 10:40