C#實現圖片自由變換 任意扭曲的演算法
之前想過要做個地鐵駕駛的遊戲,其中想把一些原本是矩形圖片弄成一個梯形,但是發現GID+上面沒有類似的方法。於是在谷歌谷了一下。沒有!只能找到令人垂涎的,並沒有原始碼。按照自己的想法嘗試了一兩天,有點效果,但實際上不是那樣。後來知道那個在數字影象處理中叫“透視變換”。於是上網找了相關資料,原理找了,看了不明白。程式碼沒多少,有ActionScript的,不明;有C的,不明。真笨啊!後來在CodeProject上面看到一份外國人的博文【http://www.codeproject.com/Articles/13201/Anti-Aliased-Image-Transformation-Aaform】,全英文看不太明白,但看了一幅圖,大概知道他意思了。下了份原始碼看看,C++的。好不容易翻譯成C#的(感覺還是保留了不少C++風格的東西),編譯通過,執行正常。後來才一步一步的閱讀程式碼。還沒全懂,先把懂的部分記錄下來。以後繼續研究繼續補充。
先看看效果
介面是仿照某個人(網上版本太多,找不到原作者)的弄出來的,介面不是重點,重點是演算法。下面就直接貼老外的那幅圖大致講講思想。
首先是從原本圖片轉化成一幅理想化的目標圖片,那幅圖片只是理想化的,最終的圖片是最右邊的那幅。轉換的過程就是根據轉換後圖片的四個角計算出目標圖片的size,生成一個矩陣,就是那個Destination Image,然後把理想化的目標圖片覆蓋過去,把理想化圖片的每個“畫素格”(已經不是真正的畫素格了,因為經過了扭曲變形)跟那個矩陣對比,看看覆蓋了哪些格子,覆蓋的面積有多少,按百分比地把顏色累加到對應的格子上。實際上那個格子就相當於新圖片的畫素點了。按照矩陣生成最終的目標圖。
接著就介紹演算法裡面呼叫的方法層次
把已經弄懂(並不代表完全懂的)的程式碼貼出來,首先是最外層的方法
1 public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc) 2 { 3 int right = 0, bottom = 0; 4 5 //主要是根據新圖片的座標,計算出圖片的寬和高,構造目標圖片的Bitmap的6 double offx = xcorner[0]; 7 double offy = ycorner[0]; 8 for (int i = 1; i < 4; i++) 9 { 10 if (xcorner[i] < offx) offx = xcorner[i]; 11 if (ycorner[i] < offy) offy = ycorner[i]; 12 } 13 14 for (int i = 0; i < 4; i++) 15 { 16 xcorner[i] -= offx; 17 ycorner[i] -= offy; 18 if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]); 19 if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]); 20 } 21 dst = new Bitmap(right, bottom); 22 Transform(src, dst, xcorner, ycorner, null); 23 }
上面這個方法只是定了目標圖片的尺寸,其餘什麼都沒做。下面這個方法還沒做多少轉換的事
1 private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc) 2 { 3 //Make sure the coordinates are valid 4 if (xcorner.Count != 4 || ycorner.Count != 4) 5 return ; 6 7 //Load the src bitmaps information 8 9 //create the intial arrays 10 //根據原圖片生成一個比原圖寬高多一個單位的圖片, 11 //這個矩陣就是用來記錄轉換後各個畫素點左上角的座標 12 pixelgrid = new AafPnt[(src.Width + 1) * (src.Height + 1)]; 13 polyoverlap = new AafPnt[16]; 14 polysorted = new AafPnt[16]; 15 corners = new AafPnt[4]; 16 17 //Load the corners array 18 double[] dx = { 0.0, 1.0, 1.0, 0.0 }; 19 double[] dy = { 0.0, 0.0, 1.0, 1.0 }; 20 for (int i = 0; i < 4; i++) 21 { 22 corners[i].x = dx[i]; 23 corners[i].y = dy[i]; 24 } 25 26 //Find the rectangle of dst to draw to 27 outstartx = rounddown(xcorner[0]); 28 outstarty = rounddown(ycorner[0]); 29 outwidth = 0; 30 outheight = 0; 31 //這裡計算出變換後起始點的座標 32 for (int i = 1; i < 4; i++) 33 { 34 if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]); 35 if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]); 36 } 37 for (int i = 0; i < 4; i++) 38 { 39 if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx); 40 if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty); 41 } 42 43 44 //fill out pixelgrid array 45 //計算出理想目標圖片中各個“畫素格”中的左上角的座標 46 if (CreateGrid(src, xcorner, ycorner)) 47 { 48 //Do the transformation 49 //進行轉換 50 DoTransform(src,dst, callbackfunc); 51 } 52 53 //Return if the function completed properly 54 return ; 55 }
下面這個方法則是計算出原影象中每個畫素點的左上角的點到目標影象中的座標,結果是存放在pixelgrid中,這個二維陣列的行和列都比原影象的寬高多1,這個關係我當初沒搞懂。用比較極限的思想,假設現在這幅圖片只有一個畫素組成,寬和高都是1,然後如果單純儲存一個左上角的座標,是無法組成一個四邊形的,這就需要把其他三個角的座標相應地記錄,這是儲存的陣列的行和列均要比原圖的寬和高多1,就是也就是一個2行2列的陣列能存放4個數值,剛好就容納了那一個畫素點的四個角的座標值。擴大到真實的圖片也同樣道理。不過這個方法我看不明白,貌似用到了向量的思想。大致是按照新圖片四條邊來計算的。
1 private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner) 2 { 3 //mmm geometry 4 double[] sideradius = new double[4]; 5 double[] sidecos = new double[4]; 6 double[] sidesin = new double[4]; 7 8 //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner 9 int j; 10 for (int i = 0; i < 4; i++) 11 { 12 j = ja[i]; 13 sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j])); 14 sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i]; 15 sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i]; 16 } 17 18 //Next we create two lines in Ax + By = C form 19 for (int x = 0; x < src.Width + 1; x++) 20 { 21 double topdist = ((double)x / (src.Width)) * sideradius[0]; 22 double ptxtop = xcorner[0] + topdist * sidecos[0]; 23 double ptytop = ycorner[0] + topdist * sidesin[0]; 24 25 double botdist = (1.0 - (double)x / (src.Width)) * sideradius[2]; 26 double ptxbot = xcorner[2] + botdist * sidecos[2]; 27 double ptybot = ycorner[2] + botdist * sidesin[2]; 28 29 double Ah = ptybot - ptytop; 30 double Bh = ptxtop - ptxbot; 31 double Ch = Ah * ptxtop + Bh * ptytop;//叉乘 32 33 for (int y = 0; y < src.Height + 1; y++) 34 { 35 double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[3]; 36 double ptxleft = xcorner[3] + leftdist * sidecos[3]; 37 double ptyleft = ycorner[3] + leftdist * sidesin[3]; 38 39 double rightdist = ((double)y / (src.Height)) * sideradius[1]; 40 double ptxright = xcorner[1] + rightdist * sidecos[1]; 41 double ptyright = ycorner[相關推薦
C#實現圖片自由變換 任意扭曲的演算法
之前想過要做個地鐵駕駛的遊戲,其中想把一些原本是矩形圖片弄成一個梯形,但是發現GID+上面沒有類似的方法。於是在谷歌谷了一下。沒有!只能找到令人垂涎的,並沒有原始碼。按照自己的想法嘗試了一兩天,有點效果,但實際上不是那樣。後來知道那個在數字影象處理中叫“透視變換”。於
用仿射變換實現圖片的角度任意旋轉
Mat src,rotate_dst; char* source_window = "Source image"; char* rotate_window = "Rotate"; int main( int argc, char** argv ) { Point2f srcTri[
c#實現圖片二值化例子(黑白效果)
rec con devel 圖片 round amp bsp 操作 spl C#將圖片2值化示例代碼,原圖及二值化後的圖片如下: 原圖: 二值化後的圖像: 實現代碼:using System; using System.Drawing; namespace BMP2G
C# 實現集合A的任意一個元素都是集合B的元素——子集
1.業務需求 存在一個集合A:{“asd”,“dss”,"254"}和集合B:{“asd”,“dss”,"254","25411"}。判斷集合A是否是集合B的子集。 2.業務分析 遍歷A集合中的每一個元素,看是否B集合中是否存在該元素。 3.演算法設計 問題:集合A是否為集合B的子集?
CSS實現圖片大小變換
我在使用CSS實現滑鼠經過時改變圖片大小使用了改變width屬性和transform屬性,發現有個問題,使用width屬性改變圖片大小時,圖片在頁面在頁面佔用的空間也會跟著變化從而影響周邊的其他內容的呈現,導致頁面變形;而使用transform變換屬性則沒有這些問題,就好像圖
C#實現圖片疊加,圖片上嵌入文字,文字生成圖片的方法
idt obi 字體 保存 poi width watermark graph col /// <summary> /// 圖片疊加 /// </summary> /// <param name="sender">
Android中利用Picasso實現圖片壓縮指定任意尺寸
之前做專案時,有個需求是指定照片壓縮到任意寬高尺寸上傳給伺服器。當時我自己寫了個圖片壓縮方法,但是不夠完美,小問題不斷(比如OOM之類的)。後來看到了神器Picasso不光能載入網路圖片,還能以任意尺寸載入本地圖片。於是我想,既然Picasso能任意尺寸載入本地圖片,那它肯
在python中實現圖片尺寸變換。
#coding=utf-8 import os import os.path from PIL import Image def ResizeImage(filein, fileout, width, height, type): img = Image.open(filein) out =
c++實現的A* 靜態尋路演算法 程式碼
在此僅提供程式碼,不對原理進行解釋。 如果想知道原理請自行百度,已經有很多前輩寫過了。 這裡用到了簡單的圖形庫 easyX #include<iostream> #include<math.h> #include<graphics.h> using n
基於OpenCV和C++實現最大閾值分割演算法
程式碼如下:: /********************************************************************************************************** *檔案說明: * 基於Ope
c++ 實現圖片轉base64
原理:原因:網路傳送渠道並不支援所有的位元組,例如傳統的郵件只支援可見字元的傳送,像ASCII碼的控制字元就不能通過郵件傳送。這樣用途就受到了很大的限制,比如圖片二進位制流的每個位元組不可能全部是可見字元,所以就傳送不了。最好的方法就是在不改變傳統協議的情 況下,做一種擴充套
C++ 實現圖片base64編解碼
最近使用人臉識別restfulAPI遇到了要求圖片base64編碼後傳輸問題。 藉此機會瞭解下什麼是base64編碼。 1 什麼是base64編碼 所謂Base64,就是說選出64個字元—-小寫字母a-z、大寫字母A-Z、數字0-9、符號”+”、”
c#實現圖片生成縮圖
1,判斷圖片的寬和高的比例,然後進行縮放,以便於使縮圖不變形。 2,DrawImage方法為縮圖繪製邊框,這樣,瀏覽起來比較美觀。 3,呼叫Save方法將生成的縮圖儲存到指定的目錄下。 private void MakeThumbnail(string source
C++實現圖片的base64編碼
1.base64編碼的原因 網路傳送渠道並不支援所有的位元組,例如傳統的郵件只支援可見字元的傳送,像ASCII碼的控制字元就不能通過郵件傳送。這樣用途就受到了很大的限制,比如圖片二進位制流的每個位元組不可能全部是可見字元,所以就傳送不了。最好的方法就是在不改變傳統協議的情
C++實現十進位制轉換為任意進位制
十進位制數在C++中用什麼表示? 一個十進位制數,在程式中用什麼方式轉換為其他進位制,和在數學中的思路一樣嗎?? 輸出的結果用什麼表示比較方便? 下面的程式中digit思考為什麼要用靜態變數? #include <iostream> #include <
C#實現圖片縮放(包括縮圖和旋轉)
using System; using System.Collections; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Web; using Syst
Objective-C實現常用的4種排序演算法
OC實現的4種排序又來了! 4種排序分別是:快速排序、氣泡排序、選擇排序、插入排序,其他的我就不寫了,因為OC裡的陣列中不能存放基本資料型別,如int不能存放,只能放物件,所以所有的資料我用了NSNumber型別,一開始我直接用>、=、<來比較結果排序後還是亂七
多圖上傳控制器及模型代碼(2)thinkphp5+layui實現多圖上傳保存到數據庫,可以實現圖片自由排序,自由刪除。
each ons param remove mon ret value settime item 公共css代碼 <style> .layui-upload-img { width: 90px; height: 90px; margin: 0;
C#實現登陸驗證碼圖片的動態生成
res pla brush rgb nal pub array ide try public ActionResult SecurityCode() { string oldcode = TempData["SecurityCode"] as string; strin
C#實現把彩色圖片灰度化代碼分享
turn pub mil int stride 方法 wid blue 節點 彩色圖片轉為灰度圖的公式如下: 代碼如下: gray(i,j) = 0.299 * Red(i,j)+0.587*Green(i,j)+0.114*Blue(i,j) 其中gray(i,j