影象處理常見問題
Matlab程式設計部分
1. Matlab 中讀、寫及顯示一幅影象的命令各是什麼?
解:第一、Matlab中讀影象函式是imread( )。imread 函式用於讀入各種影象檔案,其一般的用法為:[X,MAP]=imread(‘filename’,‘fmt’)
其中,X,MAP分別為讀出的影象資料和顏色表資料,fmt為影象的格式,filename為讀取的影象檔案(可以加上檔案的路徑)。如: [X,MAP]=imread(’flowers.tif’,’tif’);比較讀取二值影象,灰度影象,索引影象,彩色影象的X和MAP的特點。
第二、Matlab中讀影象函式是imwrite( )
按照fmt指定的格式將影象資料矩陣X和調色盤map寫入檔案filename。
第三、Matlab中顯示影象函式是imshow( ),imshow函式是最常用的顯示各種影象的函式,其語法如:imshow(X,map)
其中X是影象資料矩陣,map是其對應的顏色矩陣,若進行影象處理後不知道影象資料的值域可以用[]代替map。
- 二進位制(二值)影象顯示方法,在MATLAB中一幅二值影象是uint8或雙精度的,該矩陣僅包含0和1。如果希望工具箱中的函式能將影象理解為二進位制的,那麼所有資料都要是邏輯資料,必須對其進行設定(將所有資料標誌均設定on).可以對資料利用“~”取反操作實現影象逆轉即黑白反色。
- 灰度影象的顯示方法,正常情況下無需指定灰度影象的調色盤格式。可以是使用imshow函式指定要顯示灰度級數目,格式 imshow(I,n),n為指定的灰度級數目。使用者也可以指定資料的範圍,格式imshow(I,[low high])其中low 和high引數分別為資料陣列的最小值和最大值。如果為空矩陣([]),那麼imshow函式將自動進行資料標度。
- 索引影象,imshow(x,map)對於x的每個個畫素,imshow顯示儲存map中相應行的顏色。
- RGB影象的顯示,它直接對顏色進行描述而不使用調色盤,格式imshow(RGB)。 RGB(:,:,1) RGB(:,:,2) RGB(:,:,3)
- 特殊顯示,如多幅影象的顯示,需要顯示多幅影象時。可以使用figure語句,它的功能就是重新開啟一個影象顯示視窗.
2. Matlab 與VC++混合程式設計有哪幾種方式?
解:Matlab與VC++混合程式設計主要有三種方式:Matlab引擎方式、Matlab編譯器及COM元件。
第一種:Matlab引擎方式。Matlab引擎採用客戶機/伺服器(Client/Server)的方式,提供了一組Matlab API函式,通過呼叫這些函式實現以用程式程序之間的資料傳遞。VC程式作為前端客戶機,向Matlab引擎傳遞命令和資料,並從Matlab引擎接受資料資訊,實現動態通訊。採用這種方法幾乎能利用MATLAB全部功能,但是需要在機器上安裝MATLAB軟體,而且執行效率低,因此在實際應用中不採用這種方法,在軟體開發中也不可行。
第二種:Matlab編譯器。MATLAB Compiler可以將M語言函式檔案自動轉化產生獨立應用程式或者軟體元件,生成的元件或者獨立應用程式可以與其他使用者共享。使用MATLAB Compiler建立的獨立應用程式或者軟體元件能夠完全脫離MATLAB環境。MATLAB Compiler能夠顯著的縮短桌面應用程式的開發時間,僅僅通過簡單的指令就可以將M語言函式轉變為獨立的應用程式或者軟體元件,然後將它們打包釋出給終端使用者。這裡所指的利用M語言開發的MATLAB應用程式可以包括數學計算、圖形應用和GUIDE開發的圖形介面等,而終端使用者根本不需要擁有MATLAB。
其特點:1、自動將M語言函式檔案轉換為可執行應用程式,脫離MATLAB環境2、簡單易用的釋出過程3、支援所有MATLAB的M語言特性,例如MATLAB物件、Java物件等等。4、支援大多數工具箱函式,允許將MATLAB基本演算法免費釋出使用強大功能。 5、使用者可以利用MATLAB整合環境開發自己的演算法原型和應用程式,然後利用MATLAB Compiler將開發的演算法釋出給終端使用者,終端使用者可以通過可執行應用程式或者軟體元件應用開發完好的演算法。使用者在進行演算法維護的時候無需針對C程式碼進行操作。
演算法開發僅僅需要三個步驟:第一步:建立演算法,MATLAB本身是一種整合化的演算法開發環境,它提供了各種工具用於完成演算法快速原型的開發、測試。其中包括了高階的基於矩陣運算的向量化演算法語言M語言,並且內建了大量用於數學計算、資料分析和圖形視覺化的演算法函式。MATLAB開發工具提供了語言編輯器、除錯工具和效能分析器,並且可以利用互動式圖形介面開發工具開發自定義的圖形介面工具。第二步:轉化應用程式。使用MATLAB Compiler可以將開發好的M語言演算法函式轉變成為:獨立可執行應用程式;C/C++演算法共享庫;軟體元件,例如COM物件或者Excel外掛;獨立可執行應用程式;MATLAB Compiler可以自動地將MATLAB應用程式轉變為獨立可執行應用程式;自動確定相關的MATLAB函式 ;生成C/C++介面程式碼檔案 ;將所有相關的檔案打包壓縮儲存在單一壓縮檔案中;可在最終的應用程式中整合使用者的C或C++程式碼。第三步:演算法函式庫。使用與建立獨立可執行應用程式相同的指令就可以建立MATLAB函式庫。MATLAB Compiler將自動建立相應的標頭檔案和共享庫檔案,以便整合自定義的C/C++程式碼檔案,最終完成應用程式的開發。通過MATLAB Compiler完成應用程式的釋出之後,可以將應用程式打包、釋出給任意的終端使用者。MATLAB Compiler提供了相應的應用軟體可以將執行應用程式必需的庫檔案打包。
第三種:COM元件。COM(Component Object Model,元件物件模型)是以元件為釋出單元的物件模型,是一系列面向物件技術和工具的集合。由於COM是建立在二進位制級別上的規範,所以元件物件之間的互動規範不依賴於任何特定的語言。MATLAB提供了COM生成器。COM生成器提供了實現MATLAB獨立應用的一種新途徑。它能把MATLAB開發的演算法做成元件,這些元件作為獨立的COM物件,可以直接被C++、VB、VC、C#、JAVA或其他支援COM的語言所引用,只要相應的MATLAB編譯器和C/C+ +編譯器都已經安裝及配置成功,MATLAB COM編譯器即可開始使用,並不需要特別的設定。該方法實現簡單,通用性強,而且幾乎可以使用MATLAB的任何函式(注意:不支援指令碼檔案,指令碼檔案使用時要改為函式檔案),因此在程式較大、呼叫工具箱函式或呼叫函式較多時推薦使用。Matlab的COM 編譯器是在Matlab6.5中才開始提供的一個新工具,從Matlab7.0起,這個產品改名為MatlabBuilder for COM。基於COM的混合程式設計方法也是Mathworks公司推薦使用的方法。
以上3種方法中,採用Matlab引擎方式,應用程式整體效能好,Matlab引擎支援功能全面,但需要Matlab後臺執行,不能脫離Matlab 環境。而MCC方法和COM元件方法均可以脫離Matlab環境,應用程式執行效率高,利於軟體的開發。
3.Matlab運算中 . *和 * 的區別?
解:MATLAB中 帶“.” (讀作“點”)的運算子都表示點運算。這就要求A.*B中的A、B必須同規格,然後對應點的資料相乘,結果也是一個與A、B相同規格的矩陣。(標量是1*1矩陣)MATLAB的資料單元是矩陣,*表示的是矩陣相乘。要求A*B中A的列數等於B的行數。
影象處理基礎部分
1. Intel指令集中MMX,SSE,SSE2,SSE3和SSE4指的是什麼?
解:Intel指令集中MMX(Multi Media eXtension,多媒體擴充套件指令集)指令集是Intel公司於1996年推出的一項多媒體指令增強技術。MMX指令集中包括有57條多媒體指令,通過這些指令可以一次處理多個數據,在處理結果超過實際處理能力的時候也能進行正常處理,這樣在軟體的配合下,就可以得到更高的效能。MMX的益處在於,當時存在的作業系統不必為此而做出任何修改便可以輕鬆地執行MMX程式。但是,問題也比較明顯,那就是MMX指令集與x87浮點運算指令不能夠同時執行,必須做密集式的交錯切換才可以正常執行,這種情況就勢必造成整個系統執行質量的下降。
Intel指令集中SSE(Streaming SIMD Extensions,單指令多資料流擴充套件)指令集是Intel在Pentium III處理器中率先推出的。其實,早在PIII正式推出之前,Intel公司就曾經通過各種渠道公佈過所謂的KNI(Katmai New Instruction)指令集,這個指令集也就是SSE指令集的前身,並一度被很多傳媒稱之為MMX指令集的下一個版本,即MMX2指令集。究其背景,原來"KNI"指令集是Intel公司最早為其下一代晶片命名的指令集名稱,而所謂的"MMX2"則完全是硬體評論家們和媒體憑感覺和印象對"KNI"的 評價,Intel公司從未正式釋出過關於MMX2的訊息。而最終推出的SSE指令集也就是所謂勝出的"網際網路SSE"指令集。SSE指令集包括了70條指令,其中包含提高3D圖形運算效率的50條SIMD(單指令多資料技術)浮點運算指令、12條MMX 整數運算增強指令、8條優化記憶體中連續資料塊傳輸指令。理論上這些指令對目前流行的影象處理、浮點運算、3D運算、視訊處理、音訊處理等諸多多媒體應用起到全面強化的作用。S SE指令與3DNow!指令彼此互不相容,但SSE包含了3DNow!技術的絕大部分功能,只是實現的方法不同。SSE相容MMX指令,它可以通過SIMD和單時鐘週期並行處理多個浮點資料來有效地提高浮點運算速度。
Intel指令集中SSE2(Streaming SIMD Extensions 2,Intel官方稱為SIMD 流技術擴充套件 2或資料流單指令多資料擴充套件指令集 2)指令集是Intel公司在SSE指令集的基礎上發展起來的。相比於SSE,SSE2使用了144個新增指令,擴充套件了MMX技術和SSE技術,這些指令提高了廣大應用程式的執行效能。隨MMX技術引進的SIMD整數指令從64位擴充套件到了128 位,使SIMD整數型別操作的有效執行率成倍提高。雙倍精度浮點SIMD指令允許以 SIMD格式同時執行兩個浮點操作,提供雙倍精度操作支援有助於加速內容建立、財務、工程和科學應用。除SSE2指令之外,最初的SSE指令也得到增強,通過支援多種資料型別(例如,雙字和四字)的算術運算,支援靈活並且動態範圍更廣的計算功能。SSE2指令可讓軟體開發員極其靈活的實施演算法,並在執行諸如MPEG-2、MP3、3D圖形等之類的軟體時增強效能。Intel是從Willamette核心的Pentium 4開始支援SSE2指令集的,而AMD則是從K8架構的SledgeHammer核心的Opteron開始才支援SSE2指令集的。
Intel指令集中SSE3(Streaming SIMD Extensions 3,Intel官方稱為SIMD 流技術擴充套件 3或資料流單指令多資料擴充套件指令集 3)指令集是Intel公司在SSE2指令集的基礎上發展起來的。相比於SSE2,SSE3在SSE2的基礎上又增加了13個額外的SIMD指令。SSE3 中13個新指令的主要目的是改進執行緒同步和特定應用程式領域,例如媒體和遊戲。這些新增指令強化了處理器在浮點轉換至整數、複雜演算法、視訊編碼、SIMD浮點暫存器操作以及執行緒同步等五個方面的表現,最終達到提升多媒體和遊戲效能的目的。Intel是從Prescott核心的Pentium 4開始支援SSE3指令集的,而AMD則是從2005年下半年Troy核心的Opteron開始才支援SSE3的。但是需要注意的是,AMD所支援的SSE3與Intel的SSE3並不完全相同,主要是刪除了針對Intel超執行緒技術優化的部分指令。
Intel指令集中SSE4 (Streaming SIMD Extensions 4) 是英特爾自從SSE2之後對ISA擴充套件指令集最大的一次的升級擴充套件。新指令集增強了從多媒體應用到高效能運算應用領域的效能,同時還利用一些專用電路實現對於特定應用加速。IntelSSE4 由一套全新指令構成,旨在提升一系列應用程式的效能和能效。Intel SSE4 構建於英特爾64指令集架構(Intel64 ) (ISA)。Intel SSE4 是英特爾與其獨立軟體開發商 (ISV) 團體精誠合作的成果,它可以支援開發人員輕鬆改進產品,同時保持必要的應用級相容性,以適應處理器不斷迭代的需求。
2. 平行計算有哪些實現方式?
解:平行計算就是在平行計算或分散式計算機等高效能運算系統上所做的超級計算。實現方式有:單指令多資料流SIMD、對稱多處理機SMP、大規模並行處理機MPP、工作站機群COW、分佈共享儲存DSM多處理機。
3. 彩色影象、灰度影象、二值影象和索引影象區別?
解:彩色影象,每個畫素通常是由紅(R)、綠(G)、藍(B)三個分量來表示的,分量介於(0,255)。RGB影象與索引影象一樣都可以用來表示彩色影象。與索引影象一樣,它分別用紅(R)、綠(G)、藍(B)三原色的組合來表示每個畫素的顏色。但與索引影象不同的是,RGB影象每一個畫素的顏色值(由RGB三原色表示)直接存放在影象矩陣中,由於每一畫素的顏色需由R、G、B三個分量來表示,M、N分別表示影象的行列數,三個M x N的二維矩陣分別表示各個畫素的R、G、B三個顏色分量。RGB影象的資料型別一般為8位無符號整形,通常用於表示和存放真彩色影象,當然也可以存放灰度影象。
灰度影象(gray image)是每個畫素只有一個取樣顏色的影象,這類影象通常顯示為從最暗黑色到最亮的白色的灰度,儘管理論上這個取樣可以任何顏色的不同深淺,甚至可以是不同亮度上的不同顏色。灰度影象與黑白影象不同,在計算機影象領域中黑白影象只有黑色與白色兩種顏色;但是,灰度影象在黑色與白色之間還有許多級的顏色深度。灰度影象經常是在單個電磁波頻譜如可見光內測量每個畫素的亮度得到的,用於顯示的灰度影象通常用每個取樣畫素8位的非線性尺度來儲存,這樣可以有256級灰度(如果用16位,則有65536級)。
二值影象(binary image),即一幅二值影象的二維矩陣僅由0、1兩個值構成,“0”代表黑色,“1”代白色。由於每一畫素(矩陣中每一元素)取值僅有0、1兩種可能,所以計算機中二值影象的資料型別通常為1個二進位制位。二值影象通常用於文字、線條圖的掃描識別(OCR)和掩膜影象的儲存。
索引影象即它的檔案結構比較複雜,除了存放影象的二維矩陣外,還包括一個稱之為顏色索引矩陣MAP的二維陣列。MAP的大小由存放影象的矩陣元素值域決定,如矩陣元素值域為[0,255],則MAP矩陣的大小為256Ⅹ3,用MAP=[RGB]表示。MAP中每一行的三個元素分別指定該行對應顏色的紅、綠、藍單色值,MAP中每一行對應影象矩陣畫素的一個灰度值,如某一畫素的灰度值為64,則該畫素就與MAP中的第64行建立了對映關係,該畫素在螢幕上的實際顏色由第64行的[RGB]組合決定。也就是說,影象在螢幕上顯示時,每一畫素的顏色由存放在矩陣中該畫素的灰度值作為索引通過檢索顏色索引矩陣MAP得到。索引影象的資料型別一般為8位無符號整形(int8),相應索引矩陣MAP的大小為256Ⅹ3,因此一般索引影象只能同時顯示256種顏色,但通過改變索引矩陣,顏色的型別可以調整。索引影象的資料型別也可採用雙精度浮點型(double)。索引影象一般用於存放色彩要求比較簡單的影象,如Windows中色彩構成比較簡單的桌布多采用索引影象存放,如果影象的色彩比較複雜,就要用到RGB真彩色影象。
4. 常用邊緣檢測有哪些運算元,各有什麼特性?
解:常用邊緣檢測運算元如下所述:
- Sobel運算元,其主要用於邊緣檢測,在技術上它是以離散型的差分運算元,用來運算影象亮度函式的梯度的近似值, Sobel運算元是典型的基於一階導數的邊緣檢測運算元,由於該運算元中引入了類似區域性平均的運算,因此對噪聲具有平滑作用,能很好的消除噪聲的影響。Sobel運算元對於象素的位置的影響做了加權,與Prewitt運算元、Roberts運算元相比因此效果更好。Sobel運算元包含兩組3x3的矩陣,分別為橫向及縱向模板,將之與影象作平面卷積,即可分別得出橫向及縱向的亮度差分近似值。缺點是Sobel運算元並沒有將影象的主題與背景嚴格地區分開來,換言之就是Sobel運算元並沒有基於影象灰度進行處理,由於Sobel運算元並沒有嚴格地模擬人的視覺生理特徵,所以提取的影象輪廓有時並不能令人滿意。
- Isotropic Sobel運算元 Sobel運算元另一種形式是(Isotropic Sobel)運算元,加權平均運算元,權值反比於鄰點與中心點的距離,當沿不同方向檢測邊緣時梯度幅度一致,就是通常所說的各向同性Sobel(Isotropic Sobel)運算元。模板也有兩個,一個是檢測水平邊沿的 ,另一個是檢測垂直平邊沿的 。各向同性Sobel運算元和普通Sobel運算元相比,它的位置加權係數更為準確,在檢測不同方向的邊沿時梯度的幅度一致。
- Roberts運算元 羅伯茨運算元、Roberts運算元是一種最簡單的運算元,是一種利用區域性差分運算元尋找邊緣的運算元,他採用對角線方向相鄰兩象素之差近似梯度幅值檢測邊緣。檢測垂直邊緣的效果好於斜向邊緣,定位精度高,對噪聲敏感,無法抑制噪聲的影響。1963年,Roberts提出了這種尋找邊緣的運算元。Roberts邊緣運算元是一個2x2的模板,採用的是對角方向相鄰的兩個畫素之差。從影象處理的實際效果來看,邊緣定位較準,對噪聲敏感。適用於邊緣明顯且噪聲較少的影象分割。Roberts邊緣檢測運算元是一種利用區域性差分運算元尋找邊緣的運算元,Robert運算元影象處理後結果邊緣不是很平滑。經分析,由於Robert運算元通常會在影象邊緣附近的區域內產生較寬的響應,故採用上述運算元檢測的邊緣影象常需做細化處理,邊緣定位的精度不是很高。
- Prewitt運算元 Prewitt運算元是一種一階微分運算元的邊緣檢測,利用畫素點上下、左右鄰點的灰度差,在邊緣處達到極值檢測邊緣,去掉部分偽邊緣,對噪聲具有平滑作用 。其原理是在影象空間利用兩個方向模板與影象進行鄰域卷積來完成的,這兩個方向模板一個檢測水平邊緣,一個檢測垂直邊緣。經典Prewitt運算元認為:凡灰度新值大於或等於閾值的畫素點都是邊緣點。即選擇適當的閾值T,若P(i,j)≥T,則(i,j)為邊緣點,P(i,j)為邊緣影象。這種判定是欠合理的,會造成邊緣點的誤判,因為許多噪聲點的灰度值也很大,而且對於幅值較小的邊緣點,其邊緣反而丟失了。Prewitt運算元對噪聲有抑制作用,抑制噪聲的原理是通過畫素平均,但是畫素平均相當於對影象的低通濾波,所以Prewitt運算元對邊緣的定位不如Roberts運算元。因為平均能減少或消除噪聲,Prewitt梯度運算元法就是先求平均,再求差分來求梯度。該運算元與Sobel運算元類似,只是權值有所變化,但兩者實現起來功能還是有差距的,據經驗得知Sobel要比Prewitt更能準確檢測影象邊緣
- Laplacian運算元Laplace運算元是一種各向同性運算元,二階微分運算元,在只關心邊緣的位置而不考慮其周圍的象素灰度差值時比較合適。Laplace運算元對孤立象素的響應要比對邊緣或線的響應要更強烈,因此只適用於無噪聲圖象。存在噪聲情況下,使用Laplacian運算元檢測邊緣之前需要先進行低通濾波。所以,通常的分割演算法都是把Laplacian運算元和平滑運算元結合起來生成一個新的模板。拉普拉斯運算元也是最簡單的各向同性微分運算元,具有旋轉不變性。一個二維影象函式的拉普拉斯變換是各向同性的二階導數。拉式運算元用來改善因擴散效應的模糊特別有效,因為它符合降制模型。擴散效應是成像過程中經常發生的現象。Laplacian運算元一般不以其原始形式用於邊緣檢測,因為其作為一個二階導數,Laplacian運算元對噪聲具有無法接受的敏感性;同時其幅值產生算邊緣,這是複雜的分割不希望有的結果;最後Laplacian運算元不能檢測邊緣的方向;所以Laplacian在分割中所起的作用包括:(1)利用它的零交叉性質進行邊緣定位;(2)確定一個畫素是在一條邊緣暗的一面還是亮的一面;一般使用的是高斯型拉普拉斯運算元(Laplacian of a Gaussian,LoG),由於二階導數是線性運算,利用LoG卷積一幅影象與首先使用高斯型平滑函式卷積改影象,然後計算所得結果的拉普拉斯是一樣的。所以在LoG公式中使用高斯函式的目的就是對影象進行平滑處理,使用Laplacian運算元的目的是提供一幅用零交叉確定邊緣位置的影象;影象的平滑處理減少了噪聲的影響並且它的主要作用還是抵消由Laplacian運算元的二階導數引起的逐漸增加的噪聲影響。
- Canny運算元Canny運算元是一個具有濾波,增強,檢測的多階段的優化運算元,在進行處理前,Canny運算元先利用高斯平滑濾波器來平滑影象以除去噪聲,Canny分割演算法採用一階偏導的有限差分來計算梯度幅值和方向,在處理過程中,Canny運算元還將經過一個非極大值抑制的過程,最後Canny運算元還採用兩個閾值來連線邊緣。邊緣提取的基本問題是解決增強邊緣與抗噪能力間的矛盾,由於影象邊緣和噪聲在頻率域中同是高頻分量,簡單的微分提取運算同樣會增加影象中的噪聲,所以一般在微分運算之前應採取適當的平滑濾波,減少噪聲的影響。Canny運用嚴格的數學方法對此問題進行了分析,推匯出由# 個指數函式線性組合形式的最佳邊緣提取運算元網,其演算法的實質是用一個準高斯函式作平滑運算,然後以帶方向的一階微分定位導數最大值,Canny運算元邊緣檢測是一種比較實用的邊緣檢測運算元,具有很好的邊緣檢測效能。Canny邊緣檢測法利用高斯函式的一階微分,它能在噪聲抑制和邊緣檢測之間取得較好的平衡。
- Laplacian of Gaussian(LoG)運算元 利用影象強度二階導數的零交叉點來求邊緣點的演算法對噪聲十分敏感,所以,希望在邊緣增強前濾除噪聲.為此,將高斯濾波和拉普拉斯邊緣檢測結合在一起,形成LoG(Laplacian of Gaussian, LoG)演算法,也稱之為拉普拉斯高斯演算法.LoG邊緣檢測器的基本特徵是: 平滑濾波器是高斯濾波器.增強步驟採用二階導數(二維拉普拉斯函式).邊緣檢測判據是二階導數零交叉點並對應一階導數的較大峰值.使用線性內插方法在子畫素解析度水平上估計邊緣的位置.這種方法的特點是影象首先與高斯濾波器進行卷積,這一步既平滑了影象又降低了噪聲,孤立的噪聲點和較小的結構組織將被濾除.由於平滑會導致邊緣的延展,因此邊緣檢測器只考慮那些具有區域性梯度最大值的點為邊緣點.這一點可以用二階導數的零交叉點來實現.拉普拉斯函式用作二維二階導數的近似,是因為它是一種無方向運算元.為了避免檢測出非顯著邊緣,應選擇一階導數大於某一閾值的零交叉點作為邊緣點.
5. 簡述BP神經網路,AdBoost的基本原理?
解:BP神經網路模型處理資訊的基本原理是:輸入訊號Xi通過中間節點(隱層點)作用於輸出節點,經過非線形變換,產生輸出訊號Yk,網路訓練的每個樣本包括輸入向量X和期望輸出量t,網路輸出值Y與期望輸出值t之間的偏差,通過調整輸入節點與隱層節點的聯接強度取值Wij和隱層節點與輸出節點之間的聯接強度Tjk以及閾值,使誤差沿梯度方向下降,經過反覆學習訓練,確定與最小誤差相對應的網路引數(權值和閾值),訓練即告停止。此時經過訓練的神經網路即能對類似樣本的輸入資訊,自行處理輸出誤差最小的經過非線形轉換的資訊。
AdBoost是一個廣泛使用的BOOSTING演算法,其中訓練集上依次訓練弱分類器,每次下一個弱分類器是在訓練樣本的不同權重集合上訓練。權重是由每個樣本分類的難度確定的。分類的難度是通過分類器的輸出估計的。
C/C++部分
1. 關鍵字static的作用是什麼?
解:1)在函式體,一個被宣告為靜態的變數在這一函式被呼叫過程中維持其值不變。2)在模組內(但在函式體外),一個被宣告為靜態的變數可以被模組內所用函式訪問,但不能被模組外其它函式,它是一個本地的全域性變數。3)在模組內,一個被宣告為靜態的函式只可被這一模組的它函式呼叫。那就是,這個函式被限制在宣告它的模組的本地範圍內使用。
2. 嵌入式系統總是使用者對變數或暫存器進行位操作。給定一個整型變數a,寫兩段程式碼,第一個設定a的bit3,第二消除a的 bit 3。在以上兩個操作中,要保持其它位不變.
解:
#define BIT3(0x1<<3)
static int a;
void set_bit3(void)
{
a|=BIT3;
}
void clear_bits(void)
{
a&=~BIT3;
}
3. 簡述C,C++程式編譯的記憶體分配情況?
解:C,C++中記憶體分配方式可以分為三種:
- 從靜態儲存區域分配:記憶體在程式編譯時就已經分配好,這塊記憶體在程式的整個執行期間都存在。速度快,不容易出錯,因有系統自行管理。
- 在棧上分配:在執行函式時,函式內區域性變數的儲存單元都在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。
- 從堆上分配:即運態記憶體分配。程式在執行時候用malloc或new申請任意大小的記憶體,程式設計師自己負責在何進用free 和delete釋放記憶體。
一個C、C++程式編譯時記憶體分為5大儲存區:堆區、棧區、全域性區、文字常量區和程式程式碼區。
4. 給定一個矩陣intmaxtrixA[m][n],每行和每列都是增序的,實現一個演算法去找矩陣中的某個元素element.
解:方法一:
#include<iostream>
using namespace std;
const int M = 4;
const int N = 4;
int main
{
int matrix[M][N] = {};
double element;
int flag = 1;
for(int j=0; j<N; j++)
{
if(matrix[i][j] == element)
cout<<"位置"<<endl;
while( flag<M && matrix[i][j]<element )
--flag;
while( flag<M && matrix[i][j]>element )
++flag;
}
}
方法二:
bool Find(int *matrixA, int m, int n, int element)
{
bool found = false;
if(matrixA != NULL & m & n)
{
int i,j;
i=0;j=n-1;
while(i<m;j>=0)
{
if(maxtrixA[i*n+j] == element)
{
found = true;
break;
}
else if(matrix[i*n+j]>element
--j;
else
++i
}
}
}
5. 從1到500的500個數,第一次刪除奇數位,第二次刪除剩下來的奇數位,以此類推,最後剩下的唯一一位數是什麼?
解:比如:1,2,刪除奇數位,那剩下的是2,1,2,3,刪除奇數位,剩下的是2,1,2,3,4,剩下的是4,1,2,3,4,5,6,7,剩下的是4,1,2,3,4,5,6,7,8 和1,2,3,4,5,6,7,8,9,10,11,12,13,14,15剩下的是8,這裡有什麼規律:就是當1~n,2^i<n<2^(i+1)時候,這樣刪除剩下的是2^i。2^8<500<2^9,所以剩下的就是2^8=256。
6. 給出了一個n*n的矩形,程式設計求從左上角到右下角的路徑數(n > =2),限制只能向右或向下移動,不能回退。例如當n=2時,有6條路徑。
解:一是利用數學知識,從左上角到右下角總共要走2n步,其中橫向要走n步,所以總共就是C2n~n。二是利用遞迴實現
int getTotalPath(int m, int n)
{
if(m == 1)
return n + 1;
if(n == 1)
return m + 1;
return getTotalPath(m-1, n) + getTotalPath(m, n-1);
}
7. 給出一棵二叉樹的前序和中序遍歷,輸出後續遍歷的結果,假設二叉樹中儲存的均是ASCII碼。如前序:ABDHECFG,中序:HDBEAFCG,則輸出後序為:HDECFGCA,改正為:HDECFGBA,再次改正HDEBFGCA。
解:先利用前序和中序構建出二叉樹,然後後序遍歷輸出結果
Node* getBinaryTree(char* preOrder, char* inOrder, int len)
{
if(preOrder == NULL || *preOrder == '\0' || len<=0)
return NULL;
Node* root = (Node*) malloc(sizeof(Node));
if(root == NULL)
exit(EXIT_FAILURE);
root->data = *preOrder;
int pos = 0;
while(true)
{
if(*(inOrder+pos) == root->data)
break;
pos++;
}
if(pos == 0)
root->lchild = NULL;
else
root->lchild = getBinaryTree(preOrder+1, inOrder, pos);
if(len-pos-1 == 0)
root->rchild = NULL;
else
root->rchild = getBinaryTree(preOrder+pos+1, inOrder+pos+1,len-pos-1);
return root;
}
void postOrder(Node* root)
{
if(root == NULL)
return;
postOrder(root->lchild);
postOrder(root->rchild);
printf("%c", root->data);
}
void printPostOrderViaPreOrderAndInorder(char* preOrder, char* inOrder)
{
Node* root = getBinaryTree(preOrder, inOrder, strlen(preOrder));
postOrder(root);
}
8.自定義實現字串轉為整數的演算法,例如把“123456”轉成整數123456.(輸入中可能存在符號,和數字)
解:
enum Status {VALID,IN_VALID};
int gStatus = VALID;
int strToInt(const char* str)
{
long long result = 0;
gStatus = IN_VALID;
if(str != NULL) {
const char* digit = str;
bool minus = false;
if(*digit == '+')
digit++;
else if(*digit == '-'){
digit++;
minus = true;
}
while(*digit != '\0') {
if(*digit >= '0' && *digit <= '9'){
result = result * 10 + (*digit -'0');
if(result > std::numeric_limits<int>::max()){
result = 0;
break;
}
digit++;
}
else {
result = 0;
break;
}
}
if(*digit == '\0'){
gStatus = VALID;
if(minus)
result = 0 - result;
}
}
return static_cast<int>(result);
}
9. 求2個字串最長公共部分
解:方法一:2個迴圈,遍歷2個字串,這裡記得要回溯,2個字串都要有,否則,找不出來正確的結果。
int main(){
char a[80],b[80],c[80],d[500][80];
memset(c, 0, 80);
int i,j,m,n,t,k;
gets(a);
gets(b);//輸入字串
puts(a);
puts(b);//輸出字串
m=strlen(a);
n=strlen(b);//計算字串實際長度
int nFlag = 0;
k = 0;
int nTemp = 0;
for(i=0;i<m;i++)<span style="font-family: 'Comic Sans MS';">{</span>
nFlag = 0;
t=0;
nTemp = i;
for(j=0;j<n;j++){
if(a[i]==b[j]){
nFlag = 1;
c[t]=a[i];
t=t+1;
i=i+1;
//j=j+1;
}
else{
if (nFlag == 1){
strcpy(d[k++], c);
memset(c, 0, 80);
nFlag = 0;
t=0;
i = nTemp;
j=j-1;
}
}
}
if (nFlag == 1){
strcpy(d[k++], c);
memset(c, 0, 80);
i = nTemp;
}
}
strcpy(c,d[0]);
for(i=1;i<k-1;i++){
if(strlen(c)<strlen(d[i]))
strcpy(c,d[i]);//如果d[i]字串長度比c長就把d[i]中的字串複製給c
}
puts(c);
return 0;
}
方法二
//最長公共子序列字元個數
//c[i][j]儲存字串 {xi},{yj},(長度分別為i,j)的最長公共子序列的字元個數
//i=0或者是j=0 時,c[i][j]必定為零, i,j>=0 且 xi=yj, c[i][j]=c[i-1][j-1]+1
//若 i,j>0 但xi!=yj, c[i][j]=max{ c[i-1][j] , c[i][j-1] }
#include <stdio.h>
#include <string.h>
int c[1001][1001];
void lcs(int a, int b, char x[], char y[], int c[][1001]){
int i,j;
for(i=1;i<a;i++)
c[i][0]=0; //if b==0, set c[i][j]=0;
for(i=1;i<b;i++)
c[0][i]=0; // if a==0; set c[i][j]=0;
for(i=1;i<=a;i++) // if a!=0,b!=0 loop
for(j=1;j<=b;j++){
if(x[i-1]==y[j-1])
c[i][j]=c[i-1][j-1]+1;
else if (c[i-1][j]>=c[i][j-1])
c[i][j]=c[i-1][j];
else
c[i][j]=c[i][j-1];
}
}
int main() {
char x[1001],y[1001];
while ( scanf("%s%s",x,y)!=EOF ){
int a=strlen(x);
int b=strlen(y);
memset(c,0,sizeof(c));
lcs(a,b,x,y,c);
printf("%d\n",c[a][b]);
}
return 0;
}
10.請實現一個函式:最長順子。輸入很多個整數(1<=數值<=13),返回其中可能組成的最長的一個順子(順子中數的個數代表順的長度); 其中數字1也可以代表14;
順子包括單順\雙順\3順;單順的定義是連續5個及以上連續的數,比如1,2,3,4,5、3,4,5,6,7,8和10,11,12,13,1等; 雙順的定義是連續3個及以上連續的對(對:兩個相同的數被稱為對),比如1,1,2,2,3,3、4,4,5,5,6,6,7,7和11,11,12,12,13,13,1,1等; 3順的定義是連續2個及以上連續的3張(3張:3個相同的數被稱為3張),比如1,1,1,2,2,2、3,3,3,4,4,4,5,5,5,6,6,6和13,13,13,1,1,1等等;比如:輸入陣列[1,5,2,3,4,4,5,9,6,7,2,3,3,4], 輸出陣列[2,2,3,3,4,4,5,5]。
解:實現程式碼如下:
int putList(int k, map<int, map<int, int>* >& listData, map<int, int>* mapData){
int nFlag =0;
if (0 == k && mapData->size() >= 5){
nFlag =1;
//listData.put(mapData.size(), mapData);
listData.insert(pair <int, map<int, int>* >( mapData->size(), mapData));
}
if (1 == k && mapData->size() >= 3){
nFlag =1;
//listData.put(2 * mapData.size(), mapData);
listData.insert(pair <int, map<int, int>* >(2* mapData->size(), mapData));
}
if (2 == k && mapData->size() >= 2){
nFlag =1 ;
//listData.put(3 * mapData.size(), mapData);
listData.insert(pair <int, map<int, int>* >( 3*mapData->size(), mapData));
}
return nFlag;
}
map<int, int>* getList(int* count, int k, int num, int& nMaxCount){
map<int, map<int, int>* > listData;//= new map<int, map<int, int>*>();
map<int, int>* mapTemp = NULL;
int flag = 0;
int nRet = 0;
for (int i = 1; i < num; i++){
if (count[i] > k && flag == 0){
flag = 1;
mapTemp = new map<int, int>;
//mapTemp.put(i, count[i]);
mapTemp->insert(pair <int, int>( i, count[i]));
}
else if (count[i] > k && flag == 1) {
//mapTemp.put(i, count[i]);
mapTemp->insert(pair <int, int>( i, count[i]));
if (13 == i){
if (count[14 - i] > k) {
//mapTemp.put(14 - i, count[14 - i]);
mapTemp->insert(pair <int, int>( 14 - i, count[14 - i]));
nRet = putList(k, listData, mapTemp);
if (nRet==0){
delete mapTemp;
mapTemp = NULL;
}
}
else {
flag = 0;
nRet=putList(k, listData, mapTemp);
if (nRet==0){
delete mapTemp;
mapTemp = NULL;
}
}
}
}
else if (count[i] <= k && flag == 1){
flag = 0;
nRet=putList(k, listData, mapTemp);
if (nRet==0){
delete mapTemp;
mapTemp = NULL;
}
}
}
if (listData.size() > 0){
listData.rend();
map<int, map<int, int>* >::iterator it = listData.begin();
nMaxCount = (*it).first;
map<int, int>* mapReturn = (*it).second;
map<int, int>* maptemp;
it++;
for (; it!=listData.end(); it++){
maptemp = (*it).second;
delete maptemp;
maptemp = NULL;
}
return mapReturn;
}
else
return NULL;
}
int* GetLongeststr(int* array, int nCount, int& outCount, int MaxNum){
int* count = new int[MaxNum+1];
memset(count, 0, MaxNum*sizeof(int));
int nMaxLoop=0;
int nMaxTemp;
int nMax1Loop=0;
int nMax2Loop=0;
int nMax3Loop=0;
int nMaxkey =0;
for (int i = 0; i < nCount; i++){
if (array[i] < 1 || array[i] > MaxNum)
return NULL;
++count[array[i]];
}
map<int, map<int, int>*> allList;
map<int, int>* mapTemp = NULL;
map<int, int>* map1Temp = NULL;
map<int, int>* map2Temp = NULL;
map<int, int>* map3Temp = NULL;
for (int k = 0; k < 3; k++){
mapTemp = getList(count, k, MaxNum, nMaxTemp);
if (NULL != mapTemp){
if (0 == k) {
map1Temp = mapTemp;
nMax1Loop = nMaxTemp;
nMaxLoop=nMaxTemp;
}
else if (1 == k){
if (nMaxTemp>=nMaxLoop) {
map2Temp = mapTemp;
nMax2Loop = nMaxTemp;
nMaxLoop = nMaxTemp;
}
else{
delete mapTemp;
mapTemp =NULL;
}
}
else{
if (nMaxTemp>=nMaxLoop) {
map3Temp = mapTemp;
nMax3Loop = nMaxTemp;
nMaxLoop = nMaxTemp;
}
else{
delete mapTemp;
mapTemp =NULL;
}
}
}
}
delete[] count;
count = NULL;
if (nMaxLoop>0) {
if (nMaxLoop == nMax3Loop){
nMaxkey = 3;
mapTemp = map3Temp;
}
else if (nMaxLoop == nMax2Loop) {
nMaxkey = 2;
mapTemp = map2Temp;
}
else{
nMaxkey = 1;
mapTemp = map1Temp;
}
outCount = nMaxLoop;
int* result = new int[outCount];
int k;
int nAllCount = 0;
map<int, int>::iterator itorResult;
for (itorResult = mapTemp->begin(); itorResult!=mapTemp->end(); itorResult++){
k = itorResult->first;
for (int j =0; j<nMaxkey; j++){
result[nAllCount++] = k;
cout << itorResult->first <<",";
}
}
cout << endl;
if (map1Temp!=NULL){
delete map1Temp;
map1Temp = NULL;
}
if (map2Temp!=NULL){
delete map2Temp;
map2Temp = NULL;
}
if (map3Temp!=NULL){
delete map3Temp;
map3Temp = NULL;
}
return result;
}
else {
outCount = 0;
return NULL;
}
}