[傅里葉變換]手繪板-傅里葉繪圖Matlab小程序

1、一點(diǎn)叨叨

傅里葉原理表明:任何連續(xù)測(cè)量的時(shí)序或信號(hào),都可以表示為不同頻率的正弦波信號(hào)的無(wú)限疊加。在信號(hào)分析中,我們可以通過(guò)傅里葉變換把時(shí)域信號(hào)變換到頻域,讓我們轉(zhuǎn)換視角觀察信號(hào),同樣我們還能夠在頻域選擇我們所需要的頻率的信號(hào),而這在時(shí)域是完全做不到的。

除了時(shí)域頻域的視角,傅里葉繪圖的玩法使我們可以通過(guò)幾何方面來(lái)理解傅里葉變換。


傅里葉級(jí)數(shù)的幾何表示

如上圖所示,傅里葉級(jí)數(shù)的幾何描述便是一組首尾相連的向量,每個(gè)向量代表著傅里葉級(jí)數(shù)中的一項(xiàng),不同的向量以不同角速度旋轉(zhuǎn),向量末端的點(diǎn)在一個(gè)方向上的投影便是原信號(hào),即級(jí)數(shù)各項(xiàng)之和。而如果把向量末端看作一個(gè)坐標(biāo)點(diǎn)的話,此坐標(biāo)在相互正交的方向上投影得到的兩個(gè)信號(hào)便是此點(diǎn)在正交方向上投影長(zhǎng)度變化的描述,反過(guò)來(lái),這兩個(gè)信號(hào)組合便可以在平面上繪制此坐標(biāo)點(diǎn)繪制的曲線。

通過(guò)以上對(duì)傅里葉變換的理解,我們便可以對(duì)一組使用復(fù)坐標(biāo)描述的在二維平面上的連續(xù)點(diǎn)(間隔很?。┳鲭x散傅里葉變換,再通過(guò)離散傅里葉逆變換得到原復(fù)坐標(biāo)的一種新的表述形式,而這種形式就如上面的傅里葉級(jí)數(shù)一樣,使用一組有限個(gè)以不同角速度旋轉(zhuǎn)的首尾相連的向量來(lái)描述這些連續(xù)點(diǎn)的,基于此原理,我們可以通過(guò)Matlab編寫(xiě)程序,來(lái)實(shí)現(xiàn)通過(guò)傅里葉方法繪制曲線的功能。

主要要實(shí)現(xiàn)的就是復(fù)坐標(biāo)向量轉(zhuǎn)傅里葉繪制矩陣,通過(guò)對(duì)復(fù)坐標(biāo)向量進(jìn)行離散傅里葉變換和離散傅里葉逆變換得到有限個(gè)以不同角速度旋轉(zhuǎn)的首尾相連的向量來(lái)描述復(fù)坐標(biāo)向量,并將此繪制矩陣供給繪制函數(shù)使用


對(duì)于N個(gè)復(fù)數(shù)序列x_n,我們對(duì)其進(jìn)行離散傅里葉變換:
X_k=\sum_{k=0}^{N-1}x_n\cdot e^{-i2\pi kn/N}
N個(gè)復(fù)數(shù)序列X_k又可以進(jìn)行離散傅里葉逆變換得到:
x_n=\frac{1}{N}\sum_{k=0}^{N-1}X_k\cdot e^{i2\pi kn/N}
上式中,n=0時(shí):
x_0=\frac{1}{N}(X_0\cdot e^0+X_1\cdot e^0+X_2\cdot e^0+\cdots +X_{N-2}\cdot e^0 +X_{N-1}\cdot e^0)
n=1時(shí):
x_1=\frac{1}{N}(X_0\cdot e^0+X_1\cdot e^{\frac{i2\pi}{N}}+X_2\cdot e^{\frac{i4\pi}{N}}+\cdots +X_{N-2}\cdot e^{\frac{i2\pi (N-2)}{N}} +X_{N-1}\cdot e^{\frac{i2\pi (N-1)}{N}})
不難看出,隨著n的增大,這種描述便是有限個(gè)以不同角速度旋轉(zhuǎn)的首尾相連的向量,X_k便是那一個(gè)個(gè)向量,e^{i2\pi kn/N}便是隨著n增大時(shí)不同X_k的不同旋轉(zhuǎn)量,由于N一般取值較大,那么隨著n的增大旋轉(zhuǎn)變化比較小,可以達(dá)到一種旋轉(zhuǎn)向量連續(xù)繪制線條的動(dòng)態(tài)效果。以上代碼便是通過(guò)這一原理對(duì)初始的復(fù)坐標(biāo)向量進(jìn)行轉(zhuǎn)換,得到進(jìn)行動(dòng)態(tài)繪制的參數(shù)矩陣。


2、代碼

其實(shí)是我大學(xué)MATLAB大作業(yè)寫(xiě)的,貼出來(lái)湊點(diǎn)Cyber Junk吧。不需要.fig文件,窗口都寫(xiě)代碼里的,一個(gè)文件運(yùn)行就行。

%%                                 ----****手繪圖形轉(zhuǎn)傅里葉繪制小程序****----
%==========================================================================================================================
%    【基本功能】:“跟隨模式”記錄手繪板繪制的線條(記錄鼠標(biāo)繪制速度),使用傅里葉方法繪制線條;
%                  “邊緣模式”提取手繪板繪制圖案的邊緣,使用傅里葉方法勻速的繪制圖案邊緣。
%
%    【使用方法】:選擇繪制模式在手繪板上隨意繪制圖案,點(diǎn)擊‘傅里葉繪制’打開(kāi)窗口進(jìn)行動(dòng)態(tài)繪制,點(diǎn)擊‘清除’清除手繪板上已
%                  繪制的圖形。
%                 
%    【注    意】:點(diǎn)擊‘傅里葉繪制’后在繪制過(guò)程中無(wú)法操作手繪板窗口,等待繪制完成后再次進(jìn)行手繪板操作,傅里葉繪制如需停止
%                  請(qǐng)按<Ctrl>+<C>。
%
%==========================================================================================================================


%% 主程序(窗口設(shè)置)
function Fourier_Draw()                          %主程序,創(chuàng)建小程序窗口
%--------------------------------------------------------------------------------------------------------------------------
close all
global GUI;                                      %全局GUI變量,方便控件交互
global Fdraw_able;                               %可以進(jìn)行傅里葉繪制的判斷變量
Fdraw_able = 0;                                  %剛打開(kāi)程序,手繪板上無(wú)圖像,設(shè)置不能進(jìn)行傅里葉繪制
global axes_x axes_y axes_l;                     %主窗口可繪圖區(qū)域參數(shù)
global h_l;                                      %方形主窗口邊長(zhǎng)

h_l = 400;                                               %設(shè)置方形主窗口邊長(zhǎng)
GUI.h=figure('unit','pixels',...                         %窗口顯示單位為像素       
    'position',[100, 100, h_l, h_l,],...                 %窗口位置
    'Name','手繪板',...                                  %窗口標(biāo)題
    'Color',[0.15 0.15 0.15],...                         %窗口顏色
    'menubar','none',...                                 %窗口菜單欄不顯示
    'numbertitle','off',...                              %窗口數(shù)字標(biāo)記不顯示
    'resize','off',...                                   %窗口尺寸不可調(diào)整
    'WindowButtonDownFcn',@WindowButtonDownFcn,...       %鼠標(biāo)點(diǎn)擊窗口時(shí)調(diào)用WindowButtonDownFcn函數(shù)
    'WindowButtonMotionFcn',@WindowButtonMotionFcn,...   %鼠標(biāo)點(diǎn)擊并移動(dòng)時(shí)調(diào)用WindowButtonMotionFcn函數(shù)
    'WindowButtonUpFcn',@WindowButtonUpFcn);             %鼠標(biāo)松開(kāi)時(shí)調(diào)用WindowButtonUpFcn函數(shù)

GUI.button1 = uicontrol('Parent',GUI.h,...               %屬于GUI.h的控件
    'Style','pushbutton',...                             %控件類型為按鍵
    'String','傅里葉繪制',...                             %按鍵文字
    'Position',[h_l/8, h_l/16, h_l/4, h_l/8],...         %按鍵位置和尺寸
    'visible','on',...                                   %按鍵可視
    'FontName','黑體',...                                %文字字體
    'FontSize',10,...                                    %文字字號(hào)
    'FontWeight','bold',...                              %文字粗體
    'callback',@StartFourier_Draw);                      %點(diǎn)擊按鍵時(shí)調(diào)用StartFourier_Draw函數(shù)

GUI.button2 = uicontrol('Parent',GUI.h,...
    'Style','pushbutton',...
    'String','清除',...
    'Position',[5*h_l/8, h_l/16, h_l/4, h_l/8],...
    'visible','on',...
    'FontName','黑體',...
    'FontSize',10,...
    'FontWeight','bold',...
    'callback',@Clean_Screen);                                %點(diǎn)擊按鍵時(shí)調(diào)用Clean_Screen函數(shù)

GUI.popupmenu1 = uicontrol('Parent',GUI.h,...
    'Style','popupmenu',...                                   %控件類型為彈出欄
    'String',['跟隨模式';'邊緣模式'],...                       %彈出欄的可選項(xiàng)
    'Position',[33*h_l/80, 9*h_l/80 7*h_l/40 3*h_l/80],...
    'visible','on',...
    'FontName','黑體',...
    'FontSize',8,...
    'callback',@Clean_Screen);                                %切換選項(xiàng)時(shí)調(diào)用Clean_Screen函數(shù)

axes_x = 0.125;                                         %window1坐標(biāo)系原點(diǎn)x所在位置(窗口比例值)
axes_y = 0.2;                                           %window1坐標(biāo)系原點(diǎn)y所在位置(窗口比例值)
axes_l = 0.75;                                          %window1坐標(biāo)系長(zhǎng)和寬(窗口比例值)
GUI.window1 = axes('Parent',GUI.h,...                   %屬于GUI.h的坐標(biāo)系
    'Position',[axes_x axes_y axes_l axes_l],...
    'xtick',[],...                                      %坐標(biāo)軸無(wú)刻度
    'ytick',[],...
    'xcolor','w',...                                    %坐標(biāo)軸顏色為白色,即不可見(jiàn)
    'ycolor','w');
%--------------------------------------------------------------------------------------------------------------------------
end

%% 鼠標(biāo)點(diǎn)擊窗口反饋函數(shù)
function WindowButtonDownFcn(~,~)
%--------------------------------------------------------------------------------------------------------------------------
global GUI
global axes_l h_l;                              %window1坐標(biāo)系長(zhǎng)和寬(窗口比例值);方形主窗口邊長(zhǎng)             
set(GUI.window1,'XLim',[0,1],'YLim',[0,1])      %固定window1窗口坐標(biāo)范圍
global mode;                                    %繪制模式參數(shù)
global draw_enable;                             %手繪板可繪制標(biāo)志
global x y;                                     %存取鼠標(biāo)位置坐標(biāo)
global width;                                   %邊緣模式手繪板畫(huà)筆粗細(xì)
global D_Path;                                  %跟隨模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量存取                            
global add_point;                               %跟隨模式線條插入點(diǎn)
global Rsl;                                     %傅里葉繪制分辨率,用于減少邊緣模式繪制出現(xiàn)的鋸齒

Rsl = 1.67;                                     %設(shè)置分辨率,建議在0~3之內(nèi)
D_Path = [];                                    %清空D_Path
mode = get(GUI.popupmenu1,'Value');             %讀取彈出欄選項(xiàng),跟隨模式為1,邊緣模式為2
add_point = 5;                                  %設(shè)置插入點(diǎn),越大存取連續(xù)點(diǎn)越多,跟隨模式繪制越慢
width = 0;                                      %初始筆觸粗細(xì)為0
    position=get(gca,'currentpoint');           %獲取最近一次鼠標(biāo)位置,gca代表當(dāng)前坐標(biāo)系
    x(1)=position(1);                           %點(diǎn)擊位置橫坐標(biāo)
    y(1)=position(3);                           %點(diǎn)擊位置縱坐標(biāo)
    if  x(1)>0 && x(1)<1 && y(1)>0 && y(1)<1    %判斷點(diǎn)擊位置是否在window1內(nèi)
        draw_enable=1;                          %打開(kāi)繪制
        if mode == 1                            %跟隨模式存儲(chǔ)起點(diǎn)復(fù)坐標(biāo)
            D_Path(1) = Rsl*x(1)*axes_l*h_l + Rsl*y(1)*axes_l*h_l*1i;
        end
    end
%--------------------------------------------------------------------------------------------------------------------------    
end

%% 鼠標(biāo)點(diǎn)擊移動(dòng)反饋函數(shù)
function WindowButtonMotionFcn(~,~)
%--------------------------------------------------------------------------------------------------------------------------
global axes_l h_l;             %window1坐標(biāo)系長(zhǎng)和寬(窗口比例值);方形主窗口邊長(zhǎng)             
global mode;                   %繪制模式參數(shù)
global draw_enable;            %手繪板可繪制標(biāo)志
global Fdraw_able;             %傅里葉繪制可繪制標(biāo)志
global x y;                    %存取鼠標(biāo)位置坐標(biāo)
global width;                  %邊緣模式手繪板畫(huà)筆粗細(xì)
global D_Path;                 %跟隨模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量臨時(shí)存儲(chǔ) 
global add_point;              %跟隨模式線條插入點(diǎn)
global Rsl;                    %傅里葉繪制分辨率

if draw_enable                            %判斷是否可以繪制
Fdraw_able = 1;                           %鼠標(biāo)點(diǎn)擊并移動(dòng)手繪板上就一定有圖像,設(shè)置傅里葉繪制可繪制

    position=get(gca,'currentpoint');           %獲取最近一次鼠標(biāo)位置,gca代表當(dāng)前坐標(biāo)系
    x(end)=position(1);                         %存儲(chǔ)短線條終點(diǎn)
    y(end)=position(3);
    if mode == 1                                                %如果是跟隨模式
        if  x(end)>0 && x(end)<1  &&  y(end)>0 && y(end)<1      %如果鼠標(biāo)在window1內(nèi)
            x = linspace(x(1),x(end),add_point);                %細(xì)分線條并插入點(diǎn)
            y = linspace(y(1),y(end),add_point);
            line(x,y,'LineWidth',2,'color','k');                %繪制短線條,起點(diǎn)為前一次調(diào)用WindowButtonMotionFcn的鼠標(biāo)坐標(biāo),
                                                                %終點(diǎn)為本次調(diào)用WindowButtonMotionFcn的鼠標(biāo)坐標(biāo)
            for add = 2:add_point                               
                D_Path(end+1) =Rsl*x(add)*axes_l*h_l + Rsl*y(add)*axes_l*h_l*1i;   %跟隨模式存儲(chǔ)后續(xù)復(fù)坐標(biāo)
            end
        end
    end
    if mode == 2                                                %如果是邊緣模式
        x = linspace(x(1),x(end),10);                           %細(xì)分線條并插入8點(diǎn)
        y = linspace(y(1),y(end),10);
        if width < 4                                            %邊緣模式手繪板起始偽筆觸,筆觸慢慢變粗
            width = width + 0.5;
        end
        if  width >2 && sqrt((x(end)-x(1))*(x(end)-x(1)) + (y(end)-y(1))*(y(end)-y(1))) > 0.1
            width = width -0.7;                                 %邊緣模式手繪板鼠標(biāo)快速繪制偽筆觸,繪制過(guò)快線條會(huì)變細(xì)
        end
        line(x,y,'LineWidth',width,'color','k','Marker','.');   %繪制短線條,起點(diǎn)為前一次調(diào)用WindowButtonMotionFcn的鼠標(biāo)坐標(biāo),
                                                                %終點(diǎn)為本次調(diào)用WindowButtonMotionFcn的鼠標(biāo)坐標(biāo)
    end
    x(1)=x(end);                                                %短線條起始點(diǎn)變換為本次調(diào)用WindowButtonMotionFcn
    y(1)=y(end);
end
%--------------------------------------------------------------------------------------------------------------------------
end

%% 鼠標(biāo)松開(kāi)反饋函數(shù)
function WindowButtonUpFcn(~,~)
%--------------------------------------------------------------------------------------------------------------------------
global mode;                    %繪制模式參數(shù)
global draw_enable;             %手繪板可繪制標(biāo)志
global Follow_Lines;            %跟隨模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量存取結(jié)構(gòu)體 
global D_Path;                  %跟隨模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量臨時(shí)存儲(chǔ) 

if draw_enable                              %判斷是否可以繪制  
    if mode == 1                            %如果是跟隨模式
        Follow_Lines(end+1).path = D_Path;  %存儲(chǔ)這一筆繪制的線條
    end
end
draw_enable = 0;                            %關(guān)閉繪制
%--------------------------------------------------------------------------------------------------------------------------
end

%% 清除手寫(xiě)板函數(shù)
function Clean_Screen(~,~)
%--------------------------------------------------------------------------------------------------------------------------
global GUI;
global Fdraw_able;                       %傅里葉繪制可繪制標(biāo)志
global Follow_Lines;                     %跟隨模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量存取結(jié)構(gòu)體 
     cla(GUI.window1);                   %清除window1上的圖像
     Fdraw_able = 0;                     %設(shè)置傅里葉繪制不可繪制
     Follow_Lines = struct('path', {});  %清空跟隨模式存儲(chǔ)連續(xù)點(diǎn)復(fù)坐標(biāo)向量的結(jié)構(gòu)體 
%--------------------------------------------------------------------------------------------------------------------------
end

%% 傅里葉繪制按鍵反饋函數(shù)
function StartFourier_Draw(~,~)
%--------------------------------------------------------------------------------------------------------------------------
global GUI;
global axes_l h_l;               %window1坐標(biāo)系長(zhǎng)和寬(窗口比例值);方形主窗口邊長(zhǎng) 
global Fdraw_able;               %傅里葉繪制可繪制標(biāo)志
global mode;                     %繪制模式參數(shù)
global Follow_Lines;             %跟隨模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量存取結(jié)構(gòu)體 

if Fdraw_able == 1               %如果可以傅里葉繪制,打開(kāi)子窗口sh

GUI.sh=figure('unit','pixels',...                %窗口顯示單位為像素
   'position',[100+h_l 100 600 600],...          %窗口位置
   'Name','傅里葉繪圖',...                        %窗口標(biāo)題
   'Color',[0.15 0.15 0.15],...                  %窗口顏色
   'menubar','none',...                          %窗口菜單欄不顯示
   'numbertitle','off',...                       %窗口數(shù)字標(biāo)記不顯示
   'resize','on',...                             %窗口尺寸可調(diào)整
   'visible','on',...                            %窗口不可視
   'DeleteFcn',@Clean_Screen);                   %窗口關(guān)閉時(shí)調(diào)用Clean_Screen函數(shù)
    
pause(0.01)                                  %等待子窗口打開(kāi)
set(GUI.sh,'windowstyle','modal')            %設(shè)置子窗口打開(kāi)時(shí)主窗口不可操作
pause(0.01)                                  %等待設(shè)置完成
    
    if mode == 1                             %如果是跟隨模式
        F1 = DFT_IDFT(Follow_Lines);         %求解Follow_Line的繪制矩陣F1
        FFT_Draw(F1);                        %由繪制矩陣F1進(jìn)行動(dòng)態(tài)傅里葉繪制
    end
        
    if mode == 2                                                       %如果是邊緣模式
        cut=getframe(GUI.window1,[2, 2, axes_l*h_l-2, axes_l*h_l-2]);  %截取window1的圖片
        Graph_Lines = Pick_Point(cut.cdata);                           %提取轉(zhuǎn)換window1圖片的邊緣連續(xù)點(diǎn)為
                                                                       %連續(xù)點(diǎn)復(fù)坐標(biāo)向量結(jié)構(gòu)體Graph_Line
        F2 = DFT_IDFT(Graph_Lines);                                    %求解Follow_Line的繪制矩陣F2
        FFT_Draw(F2);                                                  %由繪制矩陣F2進(jìn)行動(dòng)態(tài)傅里葉繪制
    end
end
Follow_Lines = struct('path', {});                                     %清空跟隨模式存儲(chǔ)連續(xù)點(diǎn)復(fù)坐標(biāo)向量的結(jié)構(gòu)體 
%--------------------------------------------------------------------------------------------------------------------------
end

%% =========================================我是明顯的分割線=====================================================
%     分界線上是有關(guān)GUI設(shè)置以及控件交互的代碼,包含了少部分跟隨模式存儲(chǔ)線條的代碼,下面的代碼是數(shù)據(jù)處理與繪圖的代碼,它們是程序的
% 核心部分,程序想要展示的部分都由它們來(lái)實(shí)現(xiàn)。


%% 連續(xù)點(diǎn)提取函數(shù)(邊緣繪制模式)
function Line_Points = Pick_Point(img)
%--------------------------------------------------------------------------------------------------------------------------
global G Path;                         %預(yù)處理圖像矩陣;邊緣模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量臨時(shí)存儲(chǔ) 
global FindG;                          %尋找初始繪制點(diǎn)矩陣
global axes_l h_l;                     %window1坐標(biāo)系長(zhǎng)和寬(窗口比例值);方形主窗口邊長(zhǎng)      
global msizex msizey;                  %圖像矩陣行數(shù)和列數(shù)
global sx sy;                          %初始繪制點(diǎn)坐標(biāo)
global Rsl;                            %傅里葉繪制分辨率
msizex = Rsl*floor(axes_l*h_l);            
msizey = Rsl*floor(axes_l*h_l);

resize = imresize(img, [msizex, msizey]);         %對(duì)圖像矩陣進(jìn)行縮放
grey = rgb2gray(resize);                          %真彩圖轉(zhuǎn)換為灰度圖
B_eg = edge(grey,'Canny');                        %轉(zhuǎn)二值化圖像,邊緣為1,其他地方為0
    
Line_Points = struct('path', {});                 %邊緣模式連續(xù)點(diǎn)復(fù)坐標(biāo)向量存取結(jié)構(gòu)體,函數(shù)輸出
G = rot90(B_eg, 3);                               %eg矩陣逆時(shí)針旋轉(zhuǎn)90*3度,即順時(shí)針旋轉(zhuǎn)90度,因?yàn)榫仃囅聵?biāo)與坐標(biāo)系坐標(biāo)存在一個(gè)
FindG = rot90(B_eg, 3);                           %順時(shí)針旋轉(zhuǎn)90度的關(guān)系,存入G和FindG中
Path = [];                                        %Path清空
sx = 0;sy =0;
for x = 1:msizex                                  %1值點(diǎn)掃描
     for y = 1:msizey
        if G(x,y) ~= 0                            %找到1值點(diǎn)
            Find_StartPoint(x,y);                 %尋找此線條的初始繪制點(diǎn)
            Zchange(sx,sy);                       %將連續(xù)1值點(diǎn)轉(zhuǎn)化為連續(xù)點(diǎn)復(fù)坐標(biāo)向量        
            Line_Points(end + 1).path = Path;     %存儲(chǔ)此連續(xù)線條
            Path = [];                            %Path清空
        end
    end
end

% 尋找初始繪制點(diǎn)函數(shù) (*深度優(yōu)先遍歷算法*)
function [] = Find_StartPoint(x, y)
    dx = [0, 0,1,-1,1, 1,-1,-1,0, 0,2,-2,1,2, 2, 1,-1,-2,-2,-1,0,3, 0,-3,2, 2,-2,-2];  %搜尋向量
    dy = [1,-1,0, 0,1,-1,-1, 1,2,-2,0, 0,2,1,-1,-2,-2,-1, 1, 2,3,0,-3, 0,2,-2,-2, 2];
    FindG(x, y) = 0;                                       %當(dāng)前1值點(diǎn)清零,避免無(wú)限遞歸
    sx = x; sy = y;                                        %記錄當(dāng)前坐標(biāo)
    for scan = 1:length(dx)                                %周邊點(diǎn)掃描  
        if (x+dx(scan)>0)&&(y+dy(scan)>0)&&(x+dx(scan)<msizex)&&(y+dy(scan)<msizey)    %限制搜尋范圍在矩陣內(nèi)
            if FindG(x+dx(scan),y+dy(scan)) ~= 0           %找到臨近1值點(diǎn)
                Find_StartPoint(x+dx(scan),y+dy(scan));    %遞歸調(diào)用直到搜尋到此線條的終點(diǎn)            
                break                                      %回溯到初始點(diǎn)后跳出循環(huán),不改變已保存的線條終點(diǎn)坐標(biāo)sx,sy           
            end                                            %終點(diǎn)坐標(biāo)sx,sy便可作為繪制的起點(diǎn)坐標(biāo)
        end
    end
end

% 連續(xù)點(diǎn)轉(zhuǎn)換連續(xù)點(diǎn)復(fù)坐標(biāo)向量函數(shù) (*深度優(yōu)先遍歷算法*)
function [] = Zchange(x, y)
    dx = [0, 0,1,-1,1, 1,-1,-1,0, 0,2,-2,1,2, 2, 1,-1,-2,-2,-1,0,3, 0,-3,2, 2,-2,-2];
    dy = [1,-1,0, 0,1,-1,-1, 1,2,-2,0, 0,2,1,-1,-2,-2,-1, 1, 2,3,0,-3, 0,2,-2,-2, 2];
    G(x, y) = 0;                                       %當(dāng)前1值點(diǎn)清零,避免無(wú)限遞歸
    Path(end + 1) = x + y * 1i;                        %輪廓點(diǎn)的坐標(biāo)用復(fù)數(shù)表示
    for scan = 1:length(dx)                            %周邊點(diǎn)掃描
        if (x+dx(scan)>0)&&(y+dy(scan)>0)&&(x+dx(scan)<msizex)&&(y+dy(scan)<msizey)    %限制搜尋范圍在矩陣內(nèi)
            if G(x+dx(scan),y+dy(scan)) ~= 0           %找到臨近1值點(diǎn)
                Zchange(x+dx(scan),y+dy(scan));        %遞歸調(diào)用轉(zhuǎn)換周邊的連續(xù)1值點(diǎn)  
                break                                  %回溯到初始點(diǎn)后跳出循環(huán),防止產(chǎn)生跳躍線
            end       
        end
    end
end
%--------------------------------------------------------------------------------------------------------------------------
end

%% 連續(xù)線轉(zhuǎn)傅里葉矩陣函數(shù)(**核心原理代碼**)
function XnSet = DFT_IDFT(line_points)
%--------------------------------------------------------------------------------------------------------------------------
XnSet = struct('Xn',{},'XnSum', {});                           %用于動(dòng)態(tài)傅里葉繪制的矩陣結(jié)構(gòu)體
for serial_L = 1:length(line_points)                           %serial_L代表第serial_L條連續(xù)線
    N = length(line_points(serial_L).path);
    if N < 1000
        Xk = dft(line_points(serial_L).path,N);                %對(duì)連續(xù)點(diǎn)復(fù)向量做傅里葉變換  
    else
        Xk = fft(line_points(serial_L).path,N);
    end
    
    Wnk = exp(2i*pi*([0:length(Xk)-1]'*[0:length(Xk)-1])./ length(Xk))./ length(Xk);  %傅里葉逆變換的(W_N^-nk)/N矩陣
    
    for serial_P = 1:length(Wnk(:, 1))                         %serial_P代表第serial_P個(gè)連續(xù)點(diǎn)                  
        Wnk(serial_P,:) = Xk.* Wnk(serial_P,:);                %求傅里葉逆變換矩陣Xk*(W_N^-nk)/N
    end
                                                 %[X0*(W_N^0)/N, X1*(W_N^0)/N  , ... , Xk*(W_N^0)/N  , ... ]
    XnSet(end + 1).Xn = Wnk;            %Xn保存  %[X0*(W_N^0)/N, X1*(W_N^-1)/N , ... , Xk*(W_N^-k)/N , ... ] 矩陣
                                                 %[     ...    ,      ...      , ... ,        ...    , ... ]
                                                 %[X0*(W_N^0)/N, X1*(W_N^-n)/N , ... , Xk*(W_N^-nk)/N, ... ]
                                                     
    XnSet(end).XnSum = cumsum(Wnk, 2);  %Xn按行求累積和得到動(dòng)態(tài)繪制指向邊緣點(diǎn)的組合向量所需的矩陣
end

% 離散傅里葉變換函數(shù) ( -提高運(yùn)算速度可以直接使用fft函數(shù),這里只為把代碼原理表述的更詳盡- )
function Xk=dft(xn,N)
    xn=[xn,zeros(1,N-length(xn))];   %xn(1*N)
    n=0:N-1;
    k=0:N-1;
    WN=exp(-1j*2*pi/N);
    nk=n'*k;
    WNnk=WN.^nk;                     %W_N^nk矩陣(N*N)
    Xk=xn*WNnk;                      %Xk(1*N)
end
%--------------------------------------------------------------------------------------------------------------------------
end

%% 傅里葉動(dòng)態(tài)繪圖函數(shù)
function [] = FFT_Draw(XnSet)
%--------------------------------------------------------------------------------------------------------------------------
global axes_l h_l;                                     %window1坐標(biāo)系長(zhǎng)和寬(窗口比例值);方形主窗口邊長(zhǎng)   
global serial_color;                                   %子圓變色繪制參數(shù);
global Rsl;                                            %傅里葉繪制分辨率

for serial_L = 1:length(XnSet)                                   %第serial_L條連續(xù)線
    for serial_P = 1:ceil(Rsl):length(XnSet(serial_L).XnSum)     %第serial_P個(gè)連續(xù)點(diǎn)
        
        serial_color = 6;                              %每畫(huà)一幀子圓變色繪制參數(shù)回歸初始值,達(dá)到每個(gè)子圓的顏色一直保持不變
        clf                                            %清除serial_P-1時(shí)繪制的圖案達(dá)到動(dòng)態(tài)效果
        hold on                                        %開(kāi)啟保留繪圖痕跡
            
        if serial_L > 1                                %第serial_L條連續(xù)線繪制時(shí)保持前幾條邊緣線不消失
             for k = 1:serial_L - 1
                 if length(XnSet(k).XnSum(1,:)) >1
                    lastpoint = 2*XnSet(k).XnSum(end,end) - XnSet(k).XnSum(end-1,end);  %添加最后短線條繪制使線閉合
                    plot([XnSet(k).XnSum(:,end);lastpoint],'-k','LineWidth',2);         %最后一列即為原來(lái)的邊緣點(diǎn)復(fù)數(shù)坐標(biāo)值
                 end
            end
        end
       
        plot(XnSet(serial_L).XnSum(1:serial_P,end),'-k','LineWidth',2);                      %繪制serial_P之前的所有邊緣點(diǎn)
         
        if  serial_L~= length(XnSet) || serial_P~= length(XnSet(length(XnSet)).XnSum) -...
            mod(length(XnSet(length(XnSet)).XnSum)-1,ceil(Rsl))                             %如果不是最后的線條的最后一點(diǎn)
            plot(XnSet(serial_L).XnSum(serial_P,end),'k.','MarkerSize',10);                 %繪制組合向量筆頭點(diǎn)
            for d = 2:length(XnSet(serial_L).XnSum(serial_P,:))-1
                rd = length(XnSet(serial_L).XnSum(serial_P,:))+1-d;                         %由末端開(kāi)始繪圓使中心圓覆蓋邊緣圓
                if abs(XnSet(serial_L).Xn(serial_P,rd+1)) > 2                                       %如果Xkn向量長(zhǎng)度大于2
                    Zcircle(XnSet(serial_L).XnSum(serial_P,rd),XnSet(serial_L).Xn(serial_P,rd+1));  %繪制Xkn向量旋轉(zhuǎn)子圓
                end
            end
            if length(XnSet(serial_L).Xn(serial_P,:)) > 1                                    %如果線條不止一個(gè)點(diǎn)
                Center_circle(XnSet(serial_L).XnSum(serial_P,1),XnSet(serial_L).Xn(serial_P,2));    %繪制組合向量X0/N中心圓
            end 
            plot(XnSet(serial_L).XnSum(serial_P, :),'Color',[150,50,0]/255,'LineWidth',1.5); %繪制最終指向邊緣點(diǎn)的組合向量
            plot(XnSet(serial_L).XnSum(serial_P,1),'r.','MarkerSize',20);                    %繪制組合向量中心點(diǎn)          
            plot(XnSet(serial_L).XnSum(serial_P,1),'ro','MarkerSize',8);                     %繪制組合向量中心小圓
        end
        
        hold off                                                %關(guān)閉不保留繪圖痕跡                                 
 
        xlim([2,Rsl*axes_l*h_l-2]);ylim([2,Rsl*axes_l*h_l-2])   %限制畫(huà)框大小不變
        set(gca,'xtick',[])                                     %坐標(biāo)軸無(wú)刻度
        set(gca,'ytick',[])
        set(gca,'xcolor','w')                                   %坐標(biāo)軸顏色為白色,即不可見(jiàn)
        set(gca,'ycolor','w')
        pause(0.01/(10^Rsl))                                    %控制繪圖速度    
    end
    
    hold on                                                     %開(kāi)啟保留繪圖痕跡       
    if length(XnSet(end).XnSum(1,:)) > 1                        %如果最后一條線不是單個(gè)點(diǎn)
        end_lastpoint = 2*XnSet(end).XnSum(end,end) - XnSet(end).XnSum(end-1,end);  %添加最后一條線的最后短線條
        plot([XnSet(end).XnSum(:,end);end_lastpoint],'-k','LineWidth',2);           %繪制最后一條線
    end
    hold off                                                    %關(guān)閉保留繪圖痕跡
    
    xlim([2,Rsl*axes_l*h_l-2]);ylim([2,Rsl*axes_l*h_l-2])       %限制畫(huà)框大小不變
    set(gca,'xtick',[])                                         %坐標(biāo)軸無(wú)刻度
    set(gca,'ytick',[])
    set(gca,'xcolor','w')                                       %坐標(biāo)軸顏色為白色,即不可見(jiàn)
    set(gca,'ycolor','w')
end

% 紅色中心圓繪制函數(shù)
function [] = Center_circle(zo,zr)
    t=0:pi/20:2*pi;                                  %用多邊形繪制近似圓
    circle=zo+abs(zr)*exp(1j*t);                     %e^(j*t)即為單位圓
    plot(circle,'r','LineWidth',0.8)
end

% 彩色子圓繪制函數(shù)
function [] = Zcircle(zo,zr)
    serial_color = serial_color +1;
    serial_color = mod(serial_color,7);                                  %子圓變色繪制參數(shù)加一,使子圓有不同的顏色
    color = [0.90,0.10,0.40; 0.85,0.33,0.1; 0.30,0.75,0.93; 0.93,0.69,0.13; 0.20,0.70,0.20; 0.00,0.45,0.74; 0.49,0.18,0.56];
    t=0:pi/20:2*pi;
    circle=zo+abs(zr)*exp(1j*t);
    plot(circle,'Color',color(serial_color+1,:),'LineWidth',0.6)         %按序拾取color的RGB參數(shù)繪制不同顏色的圓
end
%--------------------------------------------------------------------------------------------------------------------------
end

%==========================================================================================================================

自己寫(xiě)的代碼用了一堆全局變量,很亂,后面用AI優(yōu)化了一下代碼:

%% 主程序(窗口設(shè)置)
function Fourier_Draw()
    close all;
    
    % 創(chuàng)建主窗口
    h_fig = figure('unit','pixels', ...
        'position',[100, 100, 400, 400], ...
        'Name','手繪板', ...
        'Color',[0.15 0.15 0.15], ...
        'menubar','none', ...
        'numbertitle','off', ...
        'resize','off', ...
        'WindowButtonDownFcn', @WindowButtonDownFcn, ...
        'WindowButtonMotionFcn', @WindowButtonMotionFcn, ...
        'WindowButtonUpFcn', @WindowButtonUpFcn);
    
    % 初始化 handles 結(jié)構(gòu)體
    handles = struct();
    handles.figure = h_fig;
    handles.h_l = 400;                       % 方形主窗口邊長(zhǎng)
    handles.axes_x = 0.125;
    handles.axes_y = 0.2;
    handles.axes_l = 0.75;
    handles.Rsl = 1.67;                      % 傅里葉繪制分辨率
    handles.add_point = 5;                   % 跟隨模式插入點(diǎn)數(shù)量
    handles.Fdraw_able = 0;                  % 是否可進(jìn)行傅里葉繪制
    handles.mode = 1;                        % 繪制模式(1:跟隨,2:邊緣)
    handles.draw_enable = 0;                 % 是否允許繪制
    handles.x = [];                          % 臨時(shí)鼠標(biāo)坐標(biāo)
    handles.y = [];
    handles.width = 0;                       % 邊緣模式筆觸粗細(xì)
    handles.D_Path = [];                     % 跟隨模式臨時(shí)點(diǎn)向量
    handles.Follow_Lines = struct('path', {}); % 跟隨模式存儲(chǔ)的線條
    
    % 創(chuàng)建坐標(biāo)系
    handles.window1 = axes('Parent', h_fig, ...
        'Position', [handles.axes_x, handles.axes_y, handles.axes_l, handles.axes_l], ...
        'xtick', [], 'ytick', [], ...
        'xcolor', 'w', 'ycolor', 'w');
    
    % 創(chuàng)建按鈕和彈出菜單
    handles.button1 = uicontrol('Parent', h_fig, ...
        'Style','pushbutton', ...
        'String','傅里葉繪制', ...
        'Position',[handles.h_l/8, handles.h_l/16, handles.h_l/4, handles.h_l/8], ...
        'FontName','黑體', 'FontSize',10, 'FontWeight','bold', ...
        'callback', @StartFourier_Draw);
    
    handles.button2 = uicontrol('Parent', h_fig, ...
        'Style','pushbutton', ...
        'String','清除', ...
        'Position',[5*handles.h_l/8, handles.h_l/16, handles.h_l/4, handles.h_l/8], ...
        'FontName','黑體', 'FontSize',10, 'FontWeight','bold', ...
        'callback', @Clean_Screen);
    
    handles.popupmenu1 = uicontrol('Parent', h_fig, ...
        'Style','popupmenu', ...
        'String',['跟隨模式';'邊緣模式'], ...
        'Position',[33*handles.h_l/80, 9*handles.h_l/80, 7*handles.h_l/40, 3*handles.h_l/80], ...
        'FontName','黑體', 'FontSize',8, ...
        'callback', @Clean_Screen);   % 切換模式時(shí)調(diào)用清除(同時(shí)更新模式)
    
    % 存儲(chǔ) handles
    guidata(h_fig, handles);
end

%% 鼠標(biāo)點(diǎn)擊窗口反饋函數(shù)
function WindowButtonDownFcn(hObject, ~)
    handles = guidata(hObject);
    set(handles.window1, 'XLim', [0,1], 'YLim', [0,1]);
    
    position = get(handles.window1, 'CurrentPoint');
    x0 = position(1,1);
    y0 = position(1,2);
    
    if x0 > 0 && x0 < 1 && y0 > 0 && y0 < 1
        handles.draw_enable = 1;
        handles.x = x0;
        handles.y = y0;
        
        if handles.mode == 1
            handles.D_Path = handles.Rsl * x0 * handles.axes_l * handles.h_l + ...
                             handles.Rsl * y0 * handles.axes_l * handles.h_l * 1i;
        end
        guidata(hObject, handles);
    end
end

%% 鼠標(biāo)點(diǎn)擊移動(dòng)反饋函數(shù)
function WindowButtonMotionFcn(hObject, ~)
    handles = guidata(hObject);
    if ~handles.draw_enable
        return;
    end
    handles.Fdraw_able = 1;   % 有圖像,允許傅里葉繪制
    
    position = get(handles.window1, 'CurrentPoint');
    x_end = position(1,1);
    y_end = position(1,2);
    
    if handles.mode == 1   % 跟隨模式
        if x_end > 0 && x_end < 1 && y_end > 0 && y_end < 1
            x_vals = linspace(handles.x, x_end, handles.add_point);
            y_vals = linspace(handles.y, y_end, handles.add_point);
            line(x_vals, y_vals, 'LineWidth',2, 'color','k');
            for add = 2:handles.add_point
                handles.D_Path(end+1) = handles.Rsl * x_vals(add) * handles.axes_l * handles.h_l + ...
                                        handles.Rsl * y_vals(add) * handles.axes_l * handles.h_l * 1i;
            end
            handles.x = x_end;
            handles.y = y_end;
        end
    elseif handles.mode == 2   % 邊緣模式
        x_vals = linspace(handles.x, x_end, 10);
        y_vals = linspace(handles.y, y_end, 10);
        if handles.width < 4
            handles.width = handles.width + 0.5;
        end
        if handles.width > 2 && sqrt((x_end - handles.x)^2 + (y_end - handles.y)^2) > 0.1
            handles.width = handles.width - 0.7;
        end
        line(x_vals, y_vals, 'LineWidth', handles.width, 'color','k', 'Marker','.');
        handles.x = x_end;
        handles.y = y_end;
    end
    guidata(hObject, handles);
end

%% 鼠標(biāo)松開(kāi)反饋函數(shù)
function WindowButtonUpFcn(hObject, ~)
    handles = guidata(hObject);
    if handles.draw_enable && handles.mode == 1
        handles.Follow_Lines(end+1).path = handles.D_Path;
    end
    handles.draw_enable = 0;
    guidata(hObject, handles);
end

%% 清除手寫(xiě)板函數(shù)(可被按鈕或子窗口關(guān)閉觸發(fā),同時(shí)處理模式切換)
function Clean_Screen(hObject, ~)
    % 判斷調(diào)用來(lái)源
    if isa(hObject, 'matlab.ui.Figure')
        % 子窗口關(guān)閉時(shí)觸發(fā)
        mainFig = get(hObject, 'UserData');
        if isempty(mainFig) || ~ishandle(mainFig)
            return;
        end
    else
        % 按鈕或彈出菜單回調(diào)
        mainFig = ancestor(hObject, 'figure');
        % 如果是彈出菜單,需要更新模式
        if strcmp(get(hObject, 'Style'), 'popupmenu')
            handles = guidata(mainFig);
            handles.mode = get(hObject, 'Value');   % 更新模式
            guidata(mainFig, handles);
        end
    end
    
    if ~ishandle(mainFig)
        return;
    end
    handles = guidata(mainFig);
    cla(handles.window1);
    handles.Fdraw_able = 0;
    handles.Follow_Lines = struct('path', {});
    guidata(mainFig, handles);
end

%% 傅里葉繪制按鍵反饋函數(shù)
function StartFourier_Draw(hObject, ~)
    mainFig = ancestor(hObject, 'figure');
    handles = guidata(mainFig);
    
    if handles.Fdraw_able == 0
        return;
    end
    
    % 創(chuàng)建子窗口
    subFig = figure('unit','pixels', ...
        'position', [100+handles.h_l, 100, 600, 600], ...
        'Name','傅里葉繪圖', ...
        'Color', [0.15 0.15 0.15], ...
        'menubar','none', ...
        'numbertitle','off', ...
        'resize','on', ...
        'visible','on', ...
        'DeleteFcn', @Clean_Screen);
    set(subFig, 'windowstyle', 'modal');
    set(subFig, 'UserData', mainFig);   % 存儲(chǔ)主窗口句柄
    
    pause(0.01);
    
    if handles.mode == 1   % 跟隨模式
        XnSet = DFT_IDFT(handles.Follow_Lines);
        FFT_Draw(XnSet, handles, subFig);
    elseif handles.mode == 2   % 邊緣模式
        % 截取主窗口圖像
        rect = [2, 2, handles.axes_l*handles.h_l-2, handles.axes_l*handles.h_l-2];
        img = getframe(handles.window1, rect);
        % 提取邊緣點(diǎn)
        Graph_Lines = Pick_Point(img.cdata, handles.Rsl, handles.axes_l, handles.h_l);
        XnSet = DFT_IDFT(Graph_Lines);
        FFT_Draw(XnSet, handles, subFig);
    end
    
    % 繪圖完成后清除跟隨模式存儲(chǔ)(子窗口關(guān)閉時(shí)也會(huì)清除)
    handles.Follow_Lines = struct('path', {});
    guidata(mainFig, handles);
end

%% ========================================= 輔助函數(shù) =====================================================

%% 連續(xù)點(diǎn)提取函數(shù)(邊緣繪制模式)
function Line_Points = Pick_Point(img, Rsl, axes_l, h_l)
    msizex = Rsl * floor(axes_l * h_l);
    msizey = msizex;
    
    resize = imresize(img, [msizex, msizey]);
    grey = rgb2gray(resize);
    B_eg = edge(grey, 'Canny');
    
    G = rot90(B_eg, 3);
    FindG = rot90(B_eg, 3);
    Line_Points = struct('path', {});
    
    % 遞歸函數(shù)(嵌套,可訪問(wèn)外部變量)
    function Find_StartPoint(x, y)
        dx = [0,0,1,-1,1,1,-1,-1,0,0,2,-2,1,2,2,1,-1,-2,-2,-1,0,3,0,-3,2,2,-2,-2];
        dy = [1,-1,0,0,1,-1,-1,1,2,-2,0,0,2,1,-1,-2,-2,-1,1,2,3,0,-3,0,2,-2,-2,2];
        FindG(x,y) = 0;
        sx = x; sy = y;
        for scan = 1:length(dx)
            nx = x + dx(scan);
            ny = y + dy(scan);
            if nx>0 && ny>0 && nx<msizex && ny<msizey && FindG(nx,ny)~=0
                Find_StartPoint(nx, ny);
                break;
            end
        end
    end
    
    function Zchange(x, y)
        dx = [0,0,1,-1,1,1,-1,-1,0,0,2,-2,1,2,2,1,-1,-2,-2,-1,0,3,0,-3,2,2,-2,-2];
        dy = [1,-1,0,0,1,-1,-1,1,2,-2,0,0,2,1,-1,-2,-2,-1,1,2,3,0,-3,0,2,-2,-2,2];
        G(x,y) = 0;
        path(end+1) = x + y*1i;
        for scan = 1:length(dx)
            nx = x + dx(scan);
            ny = y + dy(scan);
            if nx>0 && ny>0 && nx<msizex && ny<msizey && G(nx,ny)~=0
                Zchange(nx, ny);
                break;
            end
        end
    end
    
    % 掃描所有點(diǎn)
    for x = 1:msizex
        for y = 1:msizey
            if G(x,y) ~= 0
                Find_StartPoint(x, y);
                path = [];
                Zchange(x, y);
                if ~isempty(path)
                    Line_Points(end+1).path = path;
                end
            end
        end
    end
end

%% 連續(xù)線轉(zhuǎn)傅里葉矩陣函數(shù)
function XnSet = DFT_IDFT(line_points)
    XnSet = struct('Xn', {}, 'XnSum', {});
    for serial_L = 1:length(line_points)
        N = length(line_points(serial_L).path);
        if N < 1000
            Xk = dft(line_points(serial_L).path, N);
        else
            Xk = fft(line_points(serial_L).path, N);
        end
        
        Wnk = exp(2i*pi*([0:N-1]'*[0:N-1])/N) / N;
        for serial_P = 1:N
            Wnk(serial_P, :) = Xk .* Wnk(serial_P, :);
        end
        XnSet(end+1).Xn = Wnk;
        XnSet(end).XnSum = cumsum(Wnk, 2);
    end
end

function Xk = dft(xn, N)
    xn = [xn, zeros(1, N-length(xn))];
    n = 0:N-1;
    k = 0:N-1;
    WN = exp(-1j*2*pi/N);
    WNnk = WN .^ (n' * k);
    Xk = xn * WNnk;
end

%% 傅里葉動(dòng)態(tài)繪圖函數(shù)
function FFT_Draw(XnSet, handles, fig)
    % 參數(shù)提取
    axes_l = handles.axes_l;
    h_l = handles.h_l;
    Rsl = handles.Rsl;
    
    % 預(yù)定義顏色表(7種顏色)
    colorMap = [0.90,0.10,0.40; 0.85,0.33,0.1; 0.30,0.75,0.93; ...
                0.93,0.69,0.13; 0.20,0.70,0.20; 0.00,0.45,0.74; 0.49,0.18,0.56];
    
    for serial_L = 1:length(XnSet)
        for serial_P = 1:ceil(Rsl):length(XnSet(serial_L).XnSum)
            clf(fig);
            hold on;
            
            % 繪制之前已完成的線條
            if serial_L > 1
                for k = 1:serial_L-1
                    if length(XnSet(k).XnSum(1,:)) > 1
                        lastpoint = 2*XnSet(k).XnSum(end,end) - XnSet(k).XnSum(end-1,end);
                        plot(XnSet(k).XnSum(:,end), '-k', 'LineWidth', 2);
                        plot([XnSet(k).XnSum(end,end), lastpoint], '-k', 'LineWidth', 2);
                    end
                end
            end
            
            % 繪制當(dāng)前線條已完成的點(diǎn)
            plot(XnSet(serial_L).XnSum(1:serial_P, end), '-k', 'LineWidth', 2);
            
            % 如果不是最后一個(gè)點(diǎn),繪制輔助圖形
            if ~(serial_L == length(XnSet) && serial_P == length(XnSet(end).XnSum) - mod(length(XnSet(end).XnSum)-1, ceil(Rsl)))
                plot(XnSet(serial_L).XnSum(serial_P, end), 'k.', 'MarkerSize', 10);
                % 繪制向量鏈中的各個(gè)圓,顏色根據(jù)圓在鏈中的位置固定
                for d = 2:length(XnSet(serial_L).XnSum(serial_P,:))-1
                    rd = length(XnSet(serial_L).XnSum(serial_P,:)) + 1 - d;
                    if abs(XnSet(serial_L).Xn(serial_P, rd+1)) > 2
                        % 使用圓在鏈中的位置 rd 確定顏色(固定)
                        colorIdx = mod(rd, 7) + 1;
                        Zcircle(XnSet(serial_L).XnSum(serial_P, rd), ...
                                XnSet(serial_L).Xn(serial_P, rd+1), colorMap(colorIdx,:));
                    end
                end
                if length(XnSet(serial_L).Xn(serial_P,:)) > 1
                    Center_circle(XnSet(serial_L).XnSum(serial_P, 1), XnSet(serial_L).Xn(serial_P, 2));
                end
                plot(XnSet(serial_L).XnSum(serial_P, :), 'Color', [150,50,0]/255, 'LineWidth', 1.5);
                plot(XnSet(serial_L).XnSum(serial_P, 1), 'r.', 'MarkerSize', 20);
                plot(XnSet(serial_L).XnSum(serial_P, 1), 'ro', 'MarkerSize', 8);
            end
            
            hold off;
            xlim([2, Rsl*axes_l*h_l-2]);
            ylim([2, Rsl*axes_l*h_l-2]);
            set(gca, 'xtick', [], 'ytick', [], 'xcolor', 'w', 'ycolor', 'w');
            pause(0.01 / (10^Rsl));
        end
        
        % 當(dāng)前線條繪制完成,補(bǔ)充閉合線段
        hold on;
        if length(XnSet(end).XnSum(1,:)) > 1
            end_lastpoint = 2*XnSet(end).XnSum(end,end) - XnSet(end).XnSum(end-1,end);
            plot([XnSet(end).XnSum(:,end); end_lastpoint], '-k', 'LineWidth', 2);
        end
        hold off;
    end
end

%% 輔助繪圖函數(shù)
function Center_circle(zo, zr)
    t = 0:pi/20:2*pi;
    circle = zo + abs(zr) * exp(1j*t);
    plot(circle, 'r', 'LineWidth', 0.8);
end

function Zcircle(zo, zr, color)
    t = 0:pi/20:2*pi;
    circle = zo + abs(zr) * exp(1j*t);
    plot(circle, 'Color', color, 'LineWidth', 0.6);
end

3、一些現(xiàn)象

  • 【跟隨模式】會(huì)把你畫(huà)圖的速度也記錄下來(lái)(因?yàn)榇a的邏輯就是直接記錄你鼠標(biāo)的坐標(biāo)),按照和你畫(huà)圖速度同比例繪制,不是一模一樣的速度但是哪里快哪里慢是按比例來(lái)的,就類似于錄制。

  • 【邊緣模式】是使用算法提取圖像邊緣然后繪制的,輸入的是二維圖片取連續(xù)像素點(diǎn),繪制線條就是勻速的,觀感要好一些。


    動(dòng)起來(lái)就是這樣:

  • 【跟隨模式】自己把線條首尾畫(huà)閉合(起筆處與終筆處相連)就還可以:


不相連畫(huà)面就會(huì)出現(xiàn):



動(dòng)起來(lái)是這樣:


看起來(lái)不太好看,一條條直線像是連成一個(gè)光滑的曲線了,至于原因,或許可以代入一組樣本數(shù)據(jù)到公式中,看看有什么貓膩。


4、參考鏈接

  1. 鴨嘎嘎.手把手教你用傅立葉變換畫(huà)可達(dá)鴨.
    https://zhuanlan.zhihu.com/p/72632360
  2. 打浦橋程序員.一把王者榮耀的時(shí)間,讓你學(xué)會(huì)MATLAB GUI.
    https://zhuanlan.zhihu.com/p/150792841
  3. 荼荼灰.matlab 傅里葉畫(huà)任何圖.
    https://blog.csdn.net/qq_36553572/article/details/107131750
  4. Rustle.數(shù)字圖像處理:邊緣檢測(cè)(Edge detection).
    https://zhuanlan.zhihu.com/p/59640437
  5. 打個(gè)醬油_就走.MATLAB GUI設(shè)計(jì)手寫(xiě)輸入板.
    https://blog.csdn.net/hardrious/article/details/49226149

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容