1. 程式人生 > >C++及影象演算法基礎知識(一)

C++及影象演算法基礎知識(一)

PS:為了面試準備的,總結的有點粗糙。

1.receptive field:感受野

在卷積神經網路CNN中,決定某一層輸出結果中一個元素所對應的輸入層的區域大小,被稱作感受野

左圖:如果只看特徵圖,我們無法得知特徵的位置(即感受野的中心位置)和區域大小(即感受野的大小)

右圖:CNN特徵圖的大小固定,其特徵位置即感受野的中心位置。各個特徵(可以理解為影象中的畫素點)的位置在卷積後保持不變,空的部分用空白來填充。

2.虛擬函式,virtual關鍵字,c++多型

a、編譯時多型性(靜態多型):通過過載函式實現

b、執行時多型性(動態多型):通過虛擬函式實現。(宣告基類的指標,利用該指標指向任意一個子類物件,呼叫相應的虛擬函式,可以根據指向的子類的不同而實現不同的方法

。virtual關鍵字)

虛擬函式: 就是允許被其子類重新定義的成員函式

純虛擬函式: 是在基類中宣告的虛擬函式,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法

簡單記:一個介面,多種方法

作用:封裝可以使得程式碼模組化,繼承可以擴充套件已存在的程式碼,他們的目的都是為了程式碼重用。而多型的目的則是為了介面重用。

3.常見CNN結構

LeNet,AlexNet,VGG,GoogleNet,ResNet

4.物體跟蹤與識別,SLAM(重要!!!!

Simultaneous Localization and Mapping, 同時定位與地圖構建

SLAM 的目的是解決“定位”與“地圖構建”

應用:

室內掃地機

室內服務機器人

自動駕駛

空中的無人機

虛擬現實

增強現實

整個視覺SLAM流程包括以下步驟:

1.感測器資訊讀取。在視覺SLAM中主要為相機影象資訊的讀取和預處理。如果是在機器人中,還可能有碼盤、慣性感測器等資訊的讀取和同步。

2.視覺里程計(Visual Odometry,VO)。視覺里程計的任務是估算相鄰影象間相機的運動,以及區域性地圖的樣子。VO又稱為前端(Front End)。

3.後端優化(Optimization)。後端接受不同時刻視覺里程計測量的相機位姿,以及迴環檢測的資訊,對它們進行優化,得到全域性一致的軌跡和地圖。由於接在VO之後,又稱為後端(Back End)。

4.迴環檢測(Loop Closing)。迴環檢測判斷機器人是否到達過先前的位置。如果檢測到迴環,它會把資訊提供給後端進行處理。

5.建圖(Mapping)。它根據估計的軌跡,建立與任務要求對應的地圖。

經典的視覺SLAM框架是過去十幾年的研究成果。這個框架本身及其所包含的演算法已經基本定型,並且已經在許多視覺程式庫和機器人程式庫中提供。依靠這些演算法,我們能夠構建一個視覺SLAM系統,使之在正常的工作環境裡實時定位與建圖。因此,我們說,如果把工作環境限定在靜態、剛體,光照變化不明顯、沒有人為干擾的場景,那麼,這個SLAM系統是相當成熟的了。

5.梯度下降和牛頓法的區別

凸函式求極值的方法,他們都是為了求得目標函式的近似解。

梯度下降:

首先計算目標函式在當前引數值的斜率(梯度),然後乘以步長因子後帶入更新公式,如圖點所在位置(極值點右邊),此時斜率為正,那麼更新引數後引數減小,更接近極小值對應的引數。

如果更新引數後,當前引數值仍然在極值點右邊,那麼繼續上面更新,效果一樣。

如果更新引數後,當前引數值到了極值點的左邊,然後計算斜率會發現是負的,這樣經過再一次更新後就會又向著極值點的方向更新。

根據這個過程我們發現,每一步走的距離在極值點附近非常重要,如果走的步子過大,容易在極值點附近震盪而無法收斂。解決辦法:將alpha設定為隨著迭代次數而不斷減小的變數,但是也不能完全減為零。

牛頓法:

首先得明確,牛頓法是為了求解函式值為零的時候變數的取值問題的,具體地,當要求解 f(θ)=0時,如果 f可導,那麼可以通過迭代公式

當應用於求解最大似然估計的值時,變成ℓ′(θ)=0的問題。這個與梯度下降不同,梯度下降的目的是直接求解目標函式極小值,而牛頓法則變相地通過求解目標函式一階導為零的引數值,進而求得目標函式最小值。

當θ是向量時,牛頓法可以使用下面式子表示:

其中H叫做海森矩陣,其實就是目標函式對引數θ的二階導數。

海森矩陣的逆就好比梯度下降法的學習率引數alpha。牛頓法收斂速度相比梯度下降法很快,而且由於海森矩陣的的逆在迭代中不斷減小,起到逐漸縮小步長的效果。

牛頓法的缺點就是計算海森矩陣的逆比較困難,消耗時間和計算資源。因此有了擬牛頓法。

牛頓法是二階收斂,梯度下降是一階收斂,所以牛頓法就更快。如果更通俗地說的話,比如你想找一條最短的路徑走到一個盆地的最底部,梯度下降法每次只從你當前所處位置選一個坡度最大的方向走一步,牛頓法在選擇方向時,不僅會考慮坡度是否夠大,還會考慮你走了一步之後,坡度是否會變得更大。所以,可以說牛頓法比梯度下降法看得更遠一點,能更快地走到最底部。

根據wiki上的解釋,從幾何上說,牛頓法就是用一個二次曲面去擬合你當前所處位置的局部曲面,而梯度下降法是用一個平面去擬合當前的局部曲面,通常情況下,二次曲面的擬合會比平面更好,所以牛頓法選擇的下降路徑會更符合真實的最優下降路徑。

6.顏色檢測,為什麼轉換到HSV空間

按照維基百科定義,顏色是通過眼、腦和我們的生活經驗所產生的一種對光的視覺效應。人對顏色的感覺不僅僅由光的物理性質所決定,還包含心理等許多因素,比如人類對顏色的感覺往往受到周圍顏色的影響”。

當我們想要檢測特定顏色的時候,也就是需要某種“色相+飽和度”的顏色,這時使用RGB色彩空間不是一個好主意,而HSV色彩空間空間是個不錯的選擇。HSV色彩空間中,各個分量分別是Hue(色相)、飽和度(Saturation)、Value(明度),其中H和S通道對顏色有著重要的影響。題圖是當明度最大時,H(0~180)和S(0~255)的變化對顏色的影響。我們可以在根據H和S的值選擇特定的顏色。

HSV 顏色空間可以很好地把顏色資訊和亮度資訊分開,將它們放在不同的通道中,減小了光線對於特定顏色識別的影響。

7.Sobel和Laplace哪個對噪聲更敏感?怎麼在數學上證明

Laplace,二階導,對噪聲更敏感,各向同性,沒有方向資訊,sobel有垂直、水平方向

8.sobel和canny邊緣檢測的區別

sobel檢測的邊緣較粗較寬,看起來圖片是模糊的,canny檢測的邊緣較細,邊緣點定位的較準

9.編譯器工作過程

原始碼要執行,必須先轉成二進位制的機器碼:

1) 編譯預處理

2) 編譯、優化階段

3) 彙編過程

4) 連結程式

一、編譯預處理

(1)巨集定義指令(2)條件編譯指令(3) 標頭檔案包含指令

二、編譯、優化階段

經過預編譯得到的輸出檔案中,只有常量;如數字、字串、變數的定義,以及C語言的關鍵字,如main,if,else,for,while,{,}, +,-,*,\等等。

在《編譯原理》中我們可以瞭解到一個編譯器對程式程式碼的編譯主要分為下面幾個過程:

a) 詞法分析

b) 語法分析

c) 語義分析

d) 中間程式碼生成

e) 程式碼優化

f) 程式碼生成

g) 符號表管理

h) 將多個步驟組合成趟

i) 編譯器構造工具

優化處理是編譯系統中一項比較艱深的技術。它涉及到的問題不僅同編譯技術本身有關,而且同機器的硬體環境也有很大的關係。優化處理主要分為下面幾個過程:

1) 區域性優化

a) 基本塊的劃分

b) 基本塊的變換

c) 基本塊的DAG表示

d) DAG的應用

e) 構造演算法討論

2) 控制流分析和迴圈優化

a) 程式流圖與迴圈

b) 迴圈

c) 迴圈的查詢

d) 可歸約流圖

e) 迴圈優化

3) 資料流的分析與全域性優化

a) 一些主要的概念

b) 資料流方程的一般形式

c) 到達一定值資料流方程

d) 可用表示式及其資料流方程

e) 活躍變數資料流方程

f) 複寫傳播

經過優化得到的彙編程式碼必須經過彙編程式的彙編轉換成相應的機器指令,方可能被機器執行。

三、彙編過程

彙編過程實際上指把組合語言程式碼翻譯成目標機器指令的過程。

四、連結程式

由彙編程式生成的目標檔案並不能立即就被執行,其中可能還有許多沒有解決的問題。

例如,某個原始檔中的函式可能引用了另一個原始檔中定義的某個符號(如變數或者函式呼叫等);

在程式中可能呼叫了某個庫檔案中的函式,等等。所有的這些問題,都需要經連結程式的處理方能得以解決。

連結程式的主要工作就是將有關的目標檔案彼此相連線,也即將在一個檔案中引用的符號同該符號在另外一個檔案中的定義連線起來,使得所有的這些目標檔案成為一個能夠誒作業系統裝入執行的統一整體。

(1)靜態連結

在這種連結方式下,函式的程式碼將從其所在地靜態連結庫中被拷貝到最終的可執行程式中。

(2) 動態連結

在此種方式下,函式的程式碼被放到稱作是動態連結庫或共享物件的某個目標檔案中。

連結程式此時所作的只是在最終的可執行程式中記錄下共享物件的名字以及其它少量

的登記資訊。在此可執行檔案被執行時,動態連結庫的全部內容將被對映到執行時相應

程序的虛地址空間。

10.c++變數宣告

變數的宣告有兩種情況:

1、一種是需要建立儲存空間的。例如:int a 在宣告的時候就已經建立了儲存空間。

2、另一種是不需要建立儲存空間的。 例如:extern int a 其中變數a是在別的檔案中定義的。

用static來宣告一個變數的作用有二:

(1)對於區域性變數用static宣告,則是為該變數分配的空間在整個程式的執行期內都始終存在。

(2)外部變數用static來宣告,則該變數的作用只限於本檔案模組。

宣告是向編譯器介紹名字--識別符號。它告訴編譯器“這個函式或變數在某處可找到,它的模樣象什麼”。

而定義是說:“在這裡建立變數”或“在這裡建立函式”。它為名字分配儲存空間。

定義和宣告的最重要區別就是:

定義建立物件併為這個物件分配了記憶體,宣告沒有分配記憶體。

1.extern告訴編譯器變數在其他地方定義了。

extern int i; //宣告

int i; //也是定義,未初始化

2.如果宣告有初始化式,就被當作定義,即使前面加了extern

extern double pi=3.141592654; //定義 

3.函式的宣告和定義區別比較簡單,帶有{ }的就是定義,否則就是宣告。

extern double max(double d1,double d2); //宣告

static 用來控制變數的儲存方式和可見性!!!

(1)隱藏。

當我們同時編譯多個檔案時,所有未加static字首的全域性變數和函式都具有全域性可見性。

(2)static的第二個作用是保持變數內容的持久。儲存在靜態資料區的變數會在程式剛開始執行時就完成初始化,也是唯一的一次初始化。共有兩種變數儲存在靜態儲存區:全域性變數和static變數,只不過和全域性變數比起來,static可以控制變數的可見範圍,說到底static還是用來隱藏的。

(3)static的第三個作用是預設初始化為0。其實全域性變數也具備這一屬性,因為全域性變數也儲存在靜態資料區。在靜態資料區,記憶體中所有的位元組預設值都是0x00,某些時候這一特點可以減少程式設計師的工作量。比如初始化一個稀疏矩陣,我們可以一個一個地把所有元素都置0,然後把不是0的幾個元素賦值。如果定義成靜態的,就省去了一開始置0的操作。再比如要把一個字元陣列當字串來用,但又覺得每次在字元陣列末尾加’\0’太麻煩。如果把字串定義成靜態的,就省去了這個麻煩,因為那裡本來就是’\0’。

1. static全域性變數與普通的全域性變數有什麼區別 ?

  全域性變數(外部變數)的說明之前再冠以static 就構成了靜態的全域性變數。

  全域性變數本身就是靜態儲存方式, 靜態全域性變數當然也是靜態儲存方式。 這兩者在儲存方式上並無不同。

  這兩者的區別在於全域性變數的作用域是整個源程式, 當一個源程式由多個原始檔組成時,全域性變數在各個原始檔中都是有效的。 而靜態全域性變數則限制了其作用域, 即只在定義該變數的原始檔內有效, 在同一源程式的其它原始檔中不能使用它。  

    static全域性變數只初使化一次,防止在其他檔案單元中被引用;  

2.  static區域性變數和普通區域性變數有什麼區別 ?

   把區域性變數改變為靜態變數後是改變了它的儲存方式即改變了它的生存期,直到程式結束時才釋放。

  static區域性變數只被初始化一次,下一次依據上一次結果值;  

3.  static函式與普通函式有什麼區別?

   static函式與普通函式作用域不同,僅在本檔案。只在當前原始檔中使用的函式應該說明為內部函式(static修飾的函式),內部函式應該在當前原始檔中說明和定義。對於可在當前原始檔以外使用的函式,應該在一個頭檔案中說明,要使用這些函式的原始檔要包含這個標頭檔案.

  static函式在記憶體中只有一份,普通函式在每個被呼叫中維持一份拷貝

全域性變數、檔案域的靜態變數和類的靜態成員變數在main執行之前的靜態初始化過程中分配記憶體並初始化;區域性靜態變數(一般為函式內的靜態變數)在第一次使用時分配記憶體並初始化。

靜態函式:使某個函式只在一個原始檔中有效,不能被其他原始檔所用。(與靜態全域性變數一樣)

靜態函式的效果:

(1)它允其他原始檔建立並使用同名的函式,而不相互衝突。

(2) 宣告為靜態的函式不能被其他原始檔所呼叫,因為它的名字不能得到。 

static一個簡單的總結:

j告知編譯器,將變數儲存在程式的靜態儲存區而不是棧區。

(希望函式中此變數的值儲存到下一次呼叫)

k把變數的可見範圍限制在編譯單元內,使其成為一個內部連線。

對於區域性變數,已經是內部連線了,只改變其儲存方式——>靜態儲存

對於全域性變數,已經是靜態儲存了,只改變其連線方式——>內部連線

11.請說出static和const關鍵字儘可能多的作用

static:

1. 修飾普通變數,修改變數的儲存區域和生命週期,使變數儲存在靜態區,在main函式執行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統用預設值初始化它。

2. 修飾普通函式,表明函式的作用範圍,僅在定義該函式的檔案內才能使用。在多人開發專案時,為了防止與他人命令函式重名,可以將函式定位為static。

3. 修飾成員變數,修飾成員變數使所有的物件只儲存一個該變數,而且不需要生成物件就可以訪問該成員。

4. 修飾成員函式,修飾成員函式使得不需要生成物件就可以訪問該函式,但是在static函式內不能訪問非靜態成員。

const:

1. 修飾變數,說明該變數不可以被改變;

2. 修飾指標,分為指向常量的指標和指標常量;

3. 常量引用,經常用於形參型別,即避免了拷貝,又避免了函式對值的修改;

4. 修飾成員函式,說明該成員函式內不能修改成員變數。

12.char s[]和char *s 的區別