寫在前面的:
- 還以為這輩子都沒有機會把Matlab與自己的程式結合起來,因為一直都沒有時間去學習Matlab與C語言混合編程,而且也覺得那玩意兒有點難,需要很多時間;
- 還以為這輩子都沒有可能自己寫一個圖形擬合程式,因為一直覺得那個太D,認為是科學家乾的事情......
但是學習了幾天C#之後,讓我有些蠢蠢欲動,特別是作者的這句話:
“本章完成了基類的討論。仍有許多內容沒有包含進來,但前面涉及到的技術已經足夠我們建立相當複雜的應用程式了!”
而我又一直對《電腦圖形學-幾何工具演算法詳解(Geometric Tools for Computer Graphics)》這本書敬仰得五體投地,總覺得最小二乘法的直線擬合這種牛X而又基本的演算法真的很值得去親手實現一下,但一直欠於掌握的技術不夠,即手法還不夠豐富!如今,作者說我們已經可以建立相當複雜的應用程式了,恩......我可以試試了......
PS:《Geometric Tools for Computer Graphics》這本書的內容也許並不對每個人都有用,但是它的序言值得每個學者去閱讀,至少是一次(我已經看了不下百次了)。我的腦裡每天都會回憶其中的這三句:
“但是基本的原理保持不變,故而,在把有關DirectX9和Intel的64位Itanium體繫結構的書籍從書架上撤下多年以後,你很可能還會在書架上保留本書的一些版本。”
“本書中的演算法更是如此,因為它們與特定硬體、網路通訊協定或其他短暫的事物並不相關。”
“就像想獲得世界上大部分有價值的東西一樣,完全地理解本書的內容,是需要付出努力和奮鬥的。”
首先是一個.m檔案drawgraph.m,確保它能夠在Matlab裡運行。
我這裡是最小二乘法直線擬合程式。
%最小二乘法直線擬合
%Created by Safirst C. Ke 2007.8.29 Wed 14:51
function drawgraph(coords)
%傳入的參數為兩行向量,第一行為x座標,第二行為座標。
%axis ([0 100 0 100]);
grid on;
hold on;
%顯示欲擬合的點的位置
plot(coords(1,:), coords(2,:), '*');
%分解x,y座標
x = coords(1,:)
y = coords(2,:)'
b = size(coords);
c = ones(1, b(2));
MT = [c; x];
M = MT';
%f為直線函數,f = mx + b;
f = inv(MT * M) * MT * y
['y = ', num2str(f(2)), 'x + ', num2str(f(1))]
%顯示最終擬合的直線
x = -max(x):max(x);
y = f(1) + f(2) * x;
plot(x, y);
xlabel('X軸');
ylabel('Y軸');
title('最小二乘法直線擬合 by Safirst C. Ke');
legend(['y = ', num2str(f(2)), 'x + ', num2str(f(1))]);
然後將這個檔案包含在.NET的類庫工程中,並進行編譯。
這裡需要理解它的過程,畢竟.NET不能編譯.m檔案。怎麼做到的呢?
通過設定這個工程的建置事件屬性,添加為
call PlotDemoBuild.bat
然後在PlotDemoBuild.bat這個檔案裡面寫好用Matlab編譯器mcc編譯的命令列,最重要的部分就是
mcc -M -silentsetup -vg -B "dotnet:PlotDemoComp,Plotter,2.0,private" -d ../../src ../../drawgraph.m
這樣的話,點擊產生,就會通過mcc產生dll,即我們需要的類庫。
然後建立我們真正的C#工程,添加引用為剛才的類庫,並開始寫程式program.cs
using System;
using System.Collections.Generic;
using System.Text;
using MathWorks.MATLAB.NET.Utility;
using MathWorks.MATLAB.NET.Arrays;
//這兩個引用顯然要添加,不過好在這兩個命名空間屬於一個庫MWArray.dll
//C:/Program Files/MATLAB/R2007a/toolbox/dotnetbuilder/bin/win32/v2.0/MWArray.dll
using PlotDemoComp;
namespace ConsoleApplication2
{
class Program
{
//[STAThread]
static void Main(string[] args)
{
try
{
Console.WriteLine("Please Input the points you want to fit:");
string[] y = Console.ReadLine().Trim().Split();
int size = y.Length;
double[] x = new double[size];
for(int i = 0; i < size; i++)
{
x[i] = Convert.ToDouble(y[i]);
}
double[,] pointValues = new double[2, size / 2];
//從開頭算起,相鄰的兩個數為一個點,所以x和y都是間隔一個的。如1,2,3,4代表兩點(1,2),(3,4)
for(int i = 0; i < size; i += 2)
{
int index = i / 2;
pointValues[0, index] = x[i];
}
for(int i = 1; i < size; i += 2)
{
int index = (i - 1) / 2;
pointValues[1, index] = x[i];
}
Plotter plotter = new Plotter();
plotter.drawgraph((MWNumericArray)pointValues);
Console.ReadLine();
}
catch(Exception exception)
{
Console.WriteLine("Error: {0}", exception);
}
}
}
}
運行結果如下:
Please Input the points you want to fit:
1 2 3 4 5 6 -1 -2 -3 -4 -5 -6
*號標記欲擬合的點,直線為擬合直線!
就寫這麼多,以後再加入一個曲線擬合的程式。
特別聲明:在下學習.NET C#和Matlab的時間均不超過1個月,
望高手不要批評指責,照顧一下新手學習鑽研的積極性!