震驚!無聊男子竟用函數(shù)畫出可愛的卡通貓

函數(shù)共 268 頁 11783 行,前后折騰了近半個月。可惜還是有些小瑕疵,不然文章標(biāo)題就該叫《震驚!死宅竟用三角函數(shù)畫出可愛的老婆》了......

用函數(shù)畫的 Pusheen
畫 Pusheen 的函數(shù)

前言

在推上看到這樣一張圖:


Twitter截圖

覺著用函數(shù)畫妹子什么的好有趣,決定自己試著實現(xiàn)一下。

思路

從圖中可以看到,繪制妹子的函數(shù)為三角函數(shù)項的和。不難發(fā)現(xiàn),函數(shù)以 t 為參數(shù),兩個一組分別為正弦級數(shù)和余弦級數(shù),首項(a0/b0)為常數(shù),很容易看出來,函數(shù)應(yīng)該和傅里葉級數(shù)有關(guān)系。

大體思路如下:

  1. 預(yù)處理圖片。用 MATLAB 將想要用函數(shù)繪制的圖片讀入
  2. 提取輪廓
  3. 獲取輪廓的坐標(biāo)
  4. 處理坐標(biāo)。獲取到的坐標(biāo)為 N 點長的離散序列,將其進(jìn)行 N 點離散傅里葉級數(shù)展開(DFS)
  5. 驗證。將輸出結(jié)果的函數(shù)繪制成圖像,觀察效果

實現(xiàn)

先畫個簡單點的。桌面上有張可愛的 Pusheen 圖片,就用它啦。


萌萌的 Pusheen 貓

1. 預(yù)處理圖片

將 Pusheen 圖片讀入MATLAB。

cd C:\Users\Desktop
pic = imread('C:\Users\Ascend\Desktop\pusheen.jpg');

轉(zhuǎn)換為二值圖像。

pic = im2bw(pic);
show(pic)

轉(zhuǎn)換為二值圖像的原因是便于提取輪廓。看下效果。

二值圖像效果

2. 提取輪廓

這里直接調(diào)用了 MATLAB 自帶提取輪廓的算法。

pic = edge(pic,'sobel');
imshow(pic);

效果如下圖,看起來不錯,輪廓分明線條干凈。


輪廓

3. 獲取輪廓坐標(biāo)

獲取輪廓坐標(biāo),并將能夠圍成封閉曲線的坐標(biāo)分組,則每組為一個有限長的離散序列,再將每個序列進(jìn)行 DFS 展開。

獲取輪廓坐標(biāo)并不難,難在擬合封閉曲線這里。想了想毫無頭緒,上網(wǎng)搜索找到了 MATLAB 自帶的 imcontour 函數(shù)。精度一般,使用條件苛刻,后文再敘,先湊合用。

point = imcontour(pic);

得到一個 2x126494 的矩陣 point,里面有我們需要的坐標(biāo)。

4. 處理坐標(biāo)

處理矩陣

坐標(biāo)已經(jīng)得到,先試著用坐標(biāo)畫一下圖像。

width=point(2,:);
height=point(1,:);
plot(width,height)

卻得到了一副非常奇怪的圖像。

一團(tuán)亂麻

按道理,坐標(biāo)描點得到的圖像就應(yīng)該為 Pusheen 貓的輪廓。注意到圖像有幾個異常點,橫坐標(biāo)特別大。查看矩陣 point 的結(jié)構(gòu)。

矩陣 point 的結(jié)構(gòu)

第一行為縱坐標(biāo) height ,第二行為橫坐標(biāo) width,每一列為一個點 (height,width)。

第一個點的數(shù)值很奇怪。橫坐標(biāo)為 2435 ,縱坐標(biāo)為 0.1,怎么看都不是輪廓上的點。也許是某種標(biāo)記?于是向后翻了2435個點,看到第 2437 列:

2437

又是一個 (0.1,2427)。再向后翻 2427 個點:

4865

第 4865 列,又是一個 (0.1,2531)。于是這個點的意義就清楚了:橫坐標(biāo)為 0.1 的點為特殊標(biāo)記,其值為該組點的個數(shù)。依此可以將這個 2x126494 的矩陣拆成多個小矩陣,每個矩陣為一組可以擬合成封閉曲線的坐標(biāo)(imcontour函數(shù)認(rèn)為的)。

拆分矩陣

%判斷point(1,:)第一行 0.1 的個數(shù),從而判斷l(xiāng)ength的長度

length=0;

for i=1:size(point,2)
  if point(1,i)==0.1
   length=length+1;
  end
end


%開始分段,拆成a1,a2,a3,...,一共length個二維數(shù)組
for i=1:length
  temp=point(2,1);
  point(:,1)=[];
  eval(sprintf('a%d=point(1:2,1:temp);', i)); %將第i段數(shù)據(jù)送入第i個數(shù)組
  point(:,1:temp)=[];
end

%縱坐標(biāo)取負(fù)數(shù),將離散數(shù)據(jù)轉(zhuǎn)為坐標(biāo)
for i=1:length
  eval(sprintf('a%d(2,:)=-a%d(2,:);',i,i));
end

離散傅里葉級數(shù)展開

離散傅里葉級數(shù)的公式如下,根據(jù)公式,寫出 MATLAB 代碼。

DFS公式
file = fopen('outer.txt', 'w'); %I/O方式輸出結(jié)果,結(jié)果輸出到桌面的outer.txt中
for count=1:length

  eval(sprintf('a=a%d;',count));

  N = size(a,2);

  %a->ax(虛數(shù)組)

  are=a(1,:);
  aim=a(2,:);
  ax=are+aim*j;
  clear are aim;

  %A:傅里葉正變換數(shù)組

  A=[];
  for i = 0:N-1
      ang = -2 * 1j * pi * i / N;
      r = 0;
      for j=0:N-1
          r = r + exp(ang * j) * ax(j+1);
      end
      A(i+1)=r;
  end

  clear ang i j r;


  fprintf(file,['t=1:',num2str(N),';\n']);

  fprintf(file,'x(t)=');

  for i = 0:N-1
      ang = 2 * pi * i / N;
      fprintf(file,'+');
      fprintf(file,[num2str(real(A(i+1) / N)),'*cos(', num2str(ang), '*t)-']);
      fprintf(file,[num2str(imag(A(i+1) / N)),'*sin(', num2str(ang), '*t)']);
  end

  fprintf(file,';\n');

  fprintf(file,'y(t)=');
  for i = 0:N-1
      ang = 2 * pi * i / N;

      fprintf(file,[num2str(imag(A(i+1) / N)),'*cos(', num2str(ang), '*t)+']);
      fprintf(file,[num2str(real(A(i+1) / N)),'*sin(', num2str(ang), '*t)+']);
  end
  fprintf(file,'0;\n');

  fprintf(file,'plot(x,y);hold on;\n');
  
end

fclose(file);

5. 驗證

輸出的函數(shù)在桌面的 outer.txt 文件中。

結(jié)果

將函數(shù)輸入到 MATLAB 中畫圖像,結(jié)果如下。

Pusheen

驗證無誤。輸出那一堆就是能畫出可愛的 Pusheen 的函數(shù)啦 ~

其他

卡通小貓都畫出來了,打算也畫一個卡通妹子。在網(wǎng)上找了張艾米莉亞線稿

EMT

使用了用同樣的方法,結(jié)果如下。

混亂的艾米莉亞

一臉茫然,大概輪廓倒能看出來,可怎么會亂成這樣子[笑cry]

仔細(xì)想了想,原因可能出在閉合曲線分組那兒。卡通貓輪廓上的蜜汁線條也可能因為此。

對卡通貓的函數(shù)進(jìn)行 Debug:

step1
step2
step3
step4
step5

這些就差不多能看出原因了。imcontour 函數(shù)將鼻子、眼睛也認(rèn)為是外輪廓的一部分,錯誤地將其分為一組數(shù)據(jù)。對于像 Pusheen 這種線條簡單的圖形來說,精度一般,分錯組數(shù)較少。對于艾米莉亞這種復(fù)雜的人像來說,精度遠(yuǎn)遠(yuǎn)不夠,錯誤就太多了。

回到從推上看到的圖片。人家是如何畫出那么完美的人像的呢?翻了翻博主的其他推文,發(fā)現(xiàn)人家利用游戲引擎 Hot Soup Processor 設(shè)計了一個畫圖板程序,在程序上手繪一組組封閉曲線,游戲引擎會用傅里葉級數(shù)將這組封閉曲線展開為函數(shù)。這個過程不需要用程序?qū)㈦x散序列分組,精度極高。

在沒找到更好的分組方法前,就只能畫畫輪廓簡單的圖片啦[笑cry]

一些其他的函數(shù)曲線:

Twitter Curve:

twitterline

twitterfunction

1:

one

onefunction

參考資料

https://mathematica.stackexchange.com/questions/17704/how-to-create-a-new-person-curve
http://blog.wolfram.com/2013/05/17/making-formulas-for-everything-from-pi-to-the-pink-panther-to-sir-isaac-newton/
http://blog.wolframalpha.com/2013/06/10/using-formulas-for-everything-from-a-complex-analysis-class-to-political-cartoons-to-music-album-covers/
http://blog.wolfram.com/2013/08/15/even-more-formulas-for-everything-from-filled-algebraic-curves-to-the-twitter-bird-the-american-flag-chocolate-easter-bunnies-and-the-superman-solid/
http://www.isnowfy.com/generate-any-function-curve/
https://yvt.jp/contours/

Pusheen 函數(shù)下載地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容