颜色定位与偏斜扭转
EasyPR--開發詳解(5)顏色定位與偏斜扭轉
?
本篇文章介紹EasyPR里新的定位功能:顏色定位與偏斜扭正。希望這篇文檔可以幫助開發者與使用者更好的理解EasyPR的設計思想。
讓我們先看一下示例圖片,這幅圖片中的車牌通過顏色的定位法進行定位并從偏斜的視角中扭正為正視角(請看右圖的左上角)。
?
圖1 新版本的定位效果??
?
下面內容會對這兩個特性的實現過程展開具體的介紹。首先介紹顏色定位的原理,然后是偏斜扭正的實現細節。
由于本文較長,為方便讀者,以下是本文的目錄:
一.顏色定位
1.1起源
1.2方法
1.3不足與改善
二.偏斜扭正
2.1分析
2.2ROI截取
2.3擴大化旋轉
2.4偏斜判斷
2.5仿射變換
2.6總結
三.總結
?
一.?顏色定位
1.起源
在前面的介紹里,我們使用了Sobel查找垂直邊緣的方法,成功定位了許多車牌。但是,Sobel法最大的問題就在于面對垂直邊緣交錯的情況下,無法準確地定位車牌。例如下圖。為了解決這個問題,可以考慮使用顏色信息進行定位。
?
圖2 顏色定位與Sobel定位的比較
?
如果將顏色定位與Sobel定位加以結合的話,可以使車牌的定位準確率從75%上升到94%。
?
2.方法
關于顏色定位首先我們想到的解決方案就是:利用RGB值來判斷。
這個想法聽起來很自然:如果我們想找出一幅圖像中的藍色部分,那么我們只需要檢查RGB分量(RGB分量由Red分量--紅色,Green分量--綠色,Blue分量--藍色共同組成)中的Blue分量就可以了。一般來說,Blue分量是個0到255的值。如果我們設定一個閾值,并且檢查每個像素的Blue分量是否大于它,那我們不就可以得知這些像素是不是藍色的了么?這個想法雖然很好,不過存在一個問題,我們該怎么來選擇這個閾值?這是第一個問題。
即便我們用一些方法決定了閾值以后,那么下面的一個問題就會讓人抓狂,顏色是組合的,即便藍色屬性在255(這樣已經很‘藍’了吧),只要另外兩個分量配合(例如都為255),你最后得到的不是藍色,而是黑色。
這還只是區分藍色的問題,黃色更麻煩,它是由紅色和綠色組合而成的,這意味著你需要考慮兩個變量的配比問題。這些問題讓選擇RGB顏色作為判斷的難度大到難以接受的地步。因此必須另想辦法。
為了解決各種顏色相關的問題,人們發明了各種顏色模型。其中有一個模型,非常適合解決顏色判斷的問題。這個模型就是HSV模型。
圖3 HSV顏色模型
HSV模型是根據顏色的直觀特性創建的一種圓錐模型。與RGB顏色模型中的每個分量都代表一種顏色不同的是,HSV模型中每個分量并不代表一種顏色,而分別是:色調(H),飽和度(S),亮度(V)。
H分量是代表顏色特性的分量,用角度度量,取值范圍為0~360,從紅色開始按逆時針方向計算,紅色為0,綠色為120,藍色為240。S分量代表顏色的飽和信息,取值范圍為0.0~1.0,值越大,顏色越飽和。V分量代表明暗信息,取值范圍為0.0~1.0,值越大,色彩越明亮。
H分量是HSV模型中唯一跟顏色本質相關的分量。只要固定了H的值,并且保持S和V分量不太小,那么表現的顏色就會基本固定。為了判斷藍色車牌顏色的范圍,可以固定了S和V兩個值為1以后,調整H的值,然后看顏色的變化范圍。通過一段摸索,可以發現當H的取值范圍在200到280時,這些顏色都可以被認為是藍色車牌的顏色范疇。于是我們可以用H分量是否在200與280之間來決定某個像素是否屬于藍色車牌。黃色車牌也是一樣的道理,通過觀察,可以發現當H值在30到80時,顏色的值可以作為黃色車牌的顏色。
這里的顏色表來自于這個網站。
下圖顯示了藍色的H分量變化范圍。
?
圖4 藍色的H分量區間?
?
下圖顯示了黃色的H分量變化范圍。?
?
?圖5 黃色的H分量區間??
?
光判斷H分量的值是否就足夠了?
事實上是不足的。固定了H的值以后,如果移動V和S會帶來顏色的飽和度和亮度的變化。當V和S都達到最高值,也就是1時,顏色是最純正的。降低S,顏色越發趨向于變白。降低V,顏色趨向于變黑,當V為0時,顏色變為黑色。因此,S和V的值也會影響最終顏色的效果。
我們可以設置一個閾值,假設S和V都大于閾值時,顏色才屬于H所表達的顏色。
在EasyPR里,這個值是0.35,也就是V屬于0.35到1且S屬于0.35到1的一個范圍,類似于一個矩形。對V和S的閾值判斷是有必要的,因為很多車牌周身的車身,都是H分量屬于200-280,而V分量或者S分量小于0.35的。通過S和V的判斷可以排除車牌周圍車身的干擾。
?
?? ? ? ?
圖6 V和S的區間?
?
明確了使用HSV模型以及用閾值進行判斷以后,下面就是一個顏色定位的完整過程。
第一步,將圖像的顏色空間從RGB轉為HSV,在這里由于光照的影響,對于圖像使用直方圖均衡進行預處理;
第二步,依次遍歷圖像的所有像素,當H值落在200-280之間并且S值與V值也落在0.35-1.0之間,標記為白色像素,否則為黑色像素;
第三步,對僅有白黑兩個顏色的二值圖參照原先車牌定位中的方法,使用閉操作,取輪廓等方法將車牌的外接矩形截取出來做進一步的處理。
?
?圖7 藍色定位效果?
?
以上就完成了一個藍色車牌的定位過程。我們把對圖像中藍色車牌的尋找過程稱為一次與藍色模板的匹配過程。代碼中的函數稱之為colorMatch。一般說來,一幅圖像需要進行一次藍色模板的匹配,還要進行一次黃色模板的匹配,以此確保藍色和黃色的車牌都被定位出來。
黃色車牌的定位方法與其類似,僅僅只是H閾值范圍的不同。事實上,黃色定位的效果一般好的出奇,可以在非常復雜的環境下將車牌極為準確的定位出來,這可能源于現實世界中黃色非常醒目的原因。
?
??圖8 黃色定位效果?
從實際效果來看,顏色定位的效果是很好的。在通用數據測試集里,大約70%的車牌都可以被定位出來(一些顏色定位不了的,我們可以用Sobel定位處理)。
在代碼中有些細節需要注意:
一. opencv為了保證HSV三個分量都落在0-255之間(確保一個char能裝的下),對H分量除以了2,也就是0-180的范圍,S和V分量乘以了255,將0-1的范圍擴展到0-255。我們在設置閾值的時候需要參照opencv的標準,因此對參數要進行一個轉換。
二. 是v和s取值的問題。對于暗的圖來說,取值過大容易漏,而對于亮的圖,取值過小則容易跟車身混淆。因此可以考慮最適應的改變閾值。
三. 是模板問題。目前的做法是針對藍色和黃色的匹配使用了兩個模板,而不是統一的模板。統一模板的問題在于擔心藍色和黃色的干擾問題,例如黃色的車與藍色的牌的干擾,或者藍色的車和黃色牌的干擾,這里面最典型的例子就是一個帶有藍色車牌的黃色出租車,在很多城市里這已經是“標準配置”。因此需要將藍色和黃色的匹配分別用不同的模板處理。
了解完這三個細節以后,下面就是代碼部分。
View Code
3.不足
以上說明了顏色定位的設計思想與細節。那么顏色定位是不是就是萬能的?答案是否定的。在色彩充足,光照足夠的情況下,顏色定位的效果很好,但是在面對光線不足的情況,或者藍色車身的情況時,顏色定位的效果很糟糕。下圖是一輛藍色車輛,可以看出,車牌與車身內容完全重疊,無法分割。
?
圖9 失效的顏色定位?
碰到失效的顏色定位情況時需要使用原先的Sobel定位法。
目前的新版本使用了顏色定位與Sobel定位結合的方式。首先進行顏色定位,然后根據條件使用Sobel進行再次定位,增加整個系統的適應能力。
為了加強魯棒性,Sobel定位法可以用兩階段的查找。也就是在已經被Sobel定位的圖塊中,再進行一次Sobel定位。這樣可以增加準確率,但會降低了速度。一個折衷的方案是讓用戶決定一個參數m_maxPlates的值,這個值決定了你在一幅圖里最多定位多少車牌。系統首先用顏色定位出候選車牌,然后通過SVM模型來判斷是否是車牌,最后統計數量。如果這個數量大于你設定的參數,則認為車牌已經定位足夠了,不需要后一步處理,也就不會進行兩階段的Sobel查找。相反,如果這個數量不足,則繼續進行Sobel定位。
綜合定位的代碼位于CPlateDectec中的的成員函數plateDetectDeep中,以下是plateDetectDeep的整體流程。
?圖10 綜合定位全部流程?
有沒有顏色定位與Sobel定位都失效的情況?有的。這種情況下可能需要使用第三類定位技術--字符定位技術。這是EasyPR發展的一個方向,這里不展開討論。
?
二.?偏斜扭轉
解決了顏色的定位問題以后,下面的問題是:在定位以后,我們如何把偏斜過來的車牌扭正呢?
?
?
圖11 偏斜扭轉效果?
這個過程叫做偏斜扭轉過程。其中一個關鍵函數就是opencv的仿射變換函數。但在具體實施時,有很多需要解決的問題。
?
1.分析
在任何新的功能開發之前,技術預研都是第一步。
在這篇文檔介紹了opencv的仿射變換功能。效果見下圖。
圖12 仿射變換效果?
?
仔細看下,貌似這個功能跟我們的需求很相似。我們的偏斜扭轉功能,說白了,就是把對圖像的觀察視角進行了一個轉換。
不過這篇文章里的代碼基本來自于另一篇官方文檔。官方文檔里還有一個例子,可以矩形扭轉成平行四邊形。而我們的需求正是將平行四邊形的車牌扭正成矩形。這么說來,只要使用例子中對應的反函數,應該就可以實現我們的需求。從這個角度來看,偏斜扭轉功可以實現。確定了可行性以后,下一步就是思考如何實現。
在原先的版本中,我們對定位出來的區域會進行一次角度判斷,當角度小于某個閾值(默認30度)時就會進行全圖旋轉。
這種方式有兩個問題:
一是我們的策略是對整幅圖像旋轉。對于opencv來說,每次旋轉操作都是一個矩形的乘法過程,對于非常大的圖像,這個過程是非常消耗計算資源的;
二是30度的閾值無法處理示例圖片。事實上,示例圖片的定位區域的角度是-50度左右,已經大于我們的閾值了。為了處理這樣的圖片,我們需要把我們的閾值增大,例如增加到60度,那么這樣的結果是帶來候選區域的增多。
兩個因素結合,會大幅度增加處理時間。為了不讓處理速度下降,必須想辦法規避這些影響。
一個方法是不再使用全圖旋轉,而是區域旋轉。其實我們在獲取定位區域后,我們并不需要定位區域以外的圖像。
倘若我們能劃出一塊小的區域包圍定位區域,然后我們僅對定位區域進行旋轉,那么計算量就會大幅度降低。而這點,在opencv里是可以實現的,我們對定位區域RotatedRect用boundingRect()方法獲取外接矩形,再使用Mat(Rect ...)方法截取這個區域圖塊,從而生成一個小的區域圖像。于是下面的所有旋轉等操作都可以基于這個區域圖像進行。
在這些設計決定以后,下面就來思考整個功能的架構。
我們要解決的問題包括三類,第一類是正的車牌,第二類是傾斜的車牌,第三類是偏斜的車牌。前兩類是前面說過的,第三類是本次新增的功能需求。第二類傾斜車牌與第三類車牌的區別見下圖。
圖13 兩類不同的旋轉?
?
通過上圖可以看出,正視角的旋轉圖片的觀察角度仍然是正方向的,只是由于路的不平或者攝像機的傾斜等原因,導致矩形有一定傾斜。這類圖塊的特點就是在RotataedRect內部,車牌部分仍然是個矩形。偏斜視角的圖片的觀察角度是非正方向的,是從側面去看車牌。這類圖塊的特點是在RotataedRect內部,車牌部分不再是個矩形,而是一個平行四邊形。這個特性決定了我們需要區別的對待這兩類圖片。
一個初步的處理思路就是下圖。
?
圖14 分析實現流程
簡單來說,整個處理流程包括下面四步:
1.感興趣區域的截取
2.角度判斷
3.偏斜判斷
4.仿射變換?
接下來按照這四個步驟依次介紹。
2.ROI截取
如果要使用區域旋轉,首先我們必須從原圖中截取出一個包含定位區域的圖塊。
opencv提供了一個從圖像中截取感興趣區域ROI的方法,也就是Mat(Rect ...)。這個方法會在Rect所在的位置,截取原圖中一個圖塊,然后將其賦值到一個新的Mat圖像里。遺憾的是這個方法不支持RotataedRect,同時Rect與RotataedRect也沒有繼承關系。因此布不能直接調用這個方法。
我們可以使用RotataedRect的boudingRect()方法。這個方法會返回一個RotataedRect的最小外接矩形,而且這個矩形是一個Rect。因此將這個Rect傳遞給Mat(Rect...)方法就可以截取出原圖的ROI圖塊,并獲得對應的ROI圖像。
需要注意的是,ROI圖塊和ROI圖像的區別,當我們給定原圖以及一個Rect時,原圖中被Rect包圍的區域稱為ROI圖塊,此時圖塊里的坐標仍然是原圖的坐標。當這個圖塊里的內容被拷貝到一個新的Mat里時,我們稱這個新Mat為ROI圖像。ROI圖像里僅僅只包含原來圖塊里的內容,跟原圖沒有任何關系。所以圖塊和圖像雖然顯示的內容一樣,但坐標系已經發生了改變。在從ROI圖塊到ROI圖像以后,點的坐標要計算一個偏移量。
下一步的工作中可以僅對這個ROI圖像進行處理,包括對其旋轉或者變換等操作。
示例圖片中的截取出來的ROI圖像如下圖:
?
圖15 截取后的ROI圖像
在截取中可能會發生一個問題。如果直接使用boundingRect()函數的話,在運行過程中會經常發生這樣的異常。OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) incv::Mat::Mat,如下圖。
?
圖16 不安全的外接矩形函數會拋出異常
?
這個異常產生的原因在于,在opencv2.4.8中(不清楚opencv其他版本是否沒有這個問題),boundingRect()函數計算出的Rect的四個點的坐標沒有做驗證。這意味著你計算一個RotataedRect的最小外接矩形Rect時,它可能會給你一個負坐標,或者是一個超過原圖片外界的坐標。于是當你把Rect作為參數傳遞給Mat(Rect ...)的話,它會提示你所要截取的Rect中的坐標越界了!
解決方案是實現一個安全的計算最小外接矩形Rect的函數,在boundingRect()結果之上,對角點坐標進行一次判斷,如果值為負數,就置為0,如果值超過了原始Mat的rows或cols,就置為原始Mat的這些rows或cols。
這個安全函數名為calcSafeRect(...),下面是這個函數的代碼。
View Code?
3.擴大化旋轉
好,當我通過calcSafeRect(...)獲取了一個安全的Rect,然后通過Mat(Rect ...)函數截取了這個感興趣圖像ROI以后。下面的工作就是對這個新的ROI圖像進行操作。
首先是判斷這個ROI圖像是否要旋轉。為了降低工作量,我們不對角度在-5度到5度區間的ROI進行旋轉(注意這里講的角度針對的生成ROI的RotataedRect,ROI本身是水平的)。因為這么小的角度對于SVM判斷以及字符識別來說,都是沒有影響的。
對其他的角度我們需要對ROI進行旋轉。當我們對ROI進行旋轉以后,接著把轉正后的RotataedRect部分從ROI中截取出來。
但很快我們就會碰到一個新問題。讓我們看一下下圖,為什么我們截取出來的車牌區域最左邊的“川”字和右邊的“2”字發生了形變?為了搞清這個原因,作者仔細地研究了旋轉與截取函數,但很快發現了形變的根源在于旋轉后的ROI圖像。
仔細看一下旋轉后的ROI圖像,是否左右兩側不再完整,像是被截去了一部分?
?
圖17 旋轉后圖像被截斷?
?
要想理解這個問題,需要理解opencv的旋轉變換函數的特性。作為旋轉變換的核心函數,affinTransform會要求你輸出一個旋轉矩陣給它。這很簡單,因為我們只需要給它一個旋轉中心點以及角度,它就能計算出我們想要的旋轉矩陣。旋轉矩陣的獲得是通過如下的函數得到的:
Mat rot_mat = getRotationMatrix2D(new_center, angle, 1);在獲取了旋轉矩陣rot_mat,那么接下來就需要調用函數warpAffine來開始旋轉操作。這個函數的參數包括一個目標圖像、以及目標圖像的Size。目標圖像容易理解,大部分opencv的函數都會需要這個參數。我們只要新建一個Mat即可。那么目標圖像的Size是什么?在一般的觀點中,假設我們需要旋轉一個圖像,我們給opencv一個原始圖像,以及我需要在某個旋轉點對它旋轉一個角度的需求,那么opencv返回一個圖像給我即可,這個圖像的Size或者說大小應該是opencv返回給我的,為什么要我來告訴它呢?
你可以試著對一個正方形進行旋轉,仔細看看,這個正方形的外接矩形的大小會如何變化?當旋轉角度還小時,一切都還好,當角度變大時,明顯我們看到的外接矩形的大小也在擴增。在這里,外接矩形被稱為視框,也就是我需要旋轉的正方形所需要的最小區域。隨著旋轉角度的變大,視框明顯增大。
?
圖18 矩形旋轉后所需視框增大??
?
在圖像旋轉完以后,有三類點會獲得不同的處理,一種是有原圖像對應點且在視框內的,這些點被正常顯示;一類是在視框內但找不到原圖像與之對應的點,這些點被置0值(顯示為黑色);最后一類是有原圖像與之對應的點,但不在視框內的,這些點被悲慘的拋棄。
?
圖19 旋轉后三類不同點的命運?
這就是旋轉后不同三類點的命運,也就是新生成的圖像中一些點呈現黑色(被置0),一些點被截斷(被拋棄)的原因。如果把視框調整大點的話,就可以大幅度減少被截斷點的數量。所以,為了保證旋轉后的圖像不被截斷,因此我們需要計算一個合理的目標圖像的Size,讓我們的感興趣區域得到完整的顯示。
下面的代碼使用了一個極為簡單的策略,它將原始圖像與目標圖像都進行了擴大化。首先新建一個尺寸為原始圖像1.5倍的新圖像,接著把原始圖像映射到新圖像上,于是我們得到了一個顯示區域(視框)擴大化后的原始圖像。顯示區域擴大以后,那些在原圖像中沒有值的像素被置了一個初值。
接著調用warpAffine函數,使用新圖像的大小作為目標圖像的大小。warpAffine函數會將新圖像旋轉,并用目標圖像尺寸的視框去顯示它。于是我們得到了一個所有感興趣區域都被完整顯示的旋轉后圖像。
這樣,我們再使用getRectSubPix()函數就可以獲得想要的車牌區域了。
圖20 擴大化旋轉后圖像不再被截斷
以下就是旋轉函數rotation的代碼。
View Code?
4.偏斜判斷
當我們對ROI進行旋轉以后,下面一步工作就是把RotataedRect部分從ROI中截取出來,這里可以使用getRectSubPix方法,這個函數可以在被旋轉后的圖像中截取一個正的矩形圖塊出來,并賦值到一個新的Mat中,稱為車牌區域。
下步工作就是分析截取后的車牌區域。車牌區域里的車牌分為正角度和偏斜角度兩種。對于正的角度而言,可以看出車牌區域就是車牌,因此直接輸出即可。而對于偏斜角度而言,車牌是平行四邊形,與矩形的車牌區域不重合。
如何判斷一個圖像中的圖形是否是平行四邊形?
一種簡單的思路就是對圖像二值化,然后根據二值化圖像進行判斷。圖像二值化的方法有很多種,假設我們這里使用一開始在車牌定位功能中使用的大津閾值二值化法的話,效果不會太好。因為大津閾值是自適應閾值,在完整的圖像中二值出來的平行四邊形可能在小的局部圖像中就不再是。最好的辦法是使用在前面定位模塊生成后的原圖的二值圖像,我們通過同樣的操作就可以在原圖中截取一個跟車牌區域對應的二值化圖像。
下圖就是一個二值化車牌區域獲得的過程。
圖21 二值化的車牌區域
?
接下來就是對二值化車牌區域進行處理。為了判斷二值化圖像中白色的部分是平行四邊形。一種簡單的做法就是從圖像中選擇一些特定的行。計算在這個行中,第一個全為0的串的長度。從幾何意義上來看,這就是平行四邊形斜邊上某個點距離外接矩形的長度。
假設我們選擇的這些行位于二值化圖像高度的1/4,2/4,3/4處的話,如果是白色圖形是矩形的話,這些串的大小應該是相等或者相差很小的,相反如果是平行四邊形的話,那么這些串的大小應該不等,并且呈現一個遞增或遞減的關系。通過這種不同,我們就可以判斷車牌區域里的圖形,究竟是矩形還是平行四邊形。
偏斜判斷的另一個重要作用就是,計算平行四邊形傾斜的斜率,這個斜率值用來在下面的仿射變換中發揮作用。我們使用一個簡單的公式去計算這個斜率,那就是利用上面判斷過程中使用的串大小,假設二值化圖像高度的1/4,2/4,3/4處對應的串的大小分別為len1,len2,len3,車牌區域的高度為Height。一個計算斜率slope的計算公式就是:(len3-len1)/Height*2。
Slope的直觀含義見下圖。
?
?
?圖22 slope的幾何含義
?
需要說明的,這個計算結果在平行四邊形是右斜時是負值,而在左斜時則是正值。于是可以根據slope的正負判斷平行四邊形是右斜或者左斜。在實踐中,會發生一些公式不能應對的情況,例如像下圖這種情況,斜邊的部分區域發生了內凹或者外凸現象。這種現象會導致len1,len2或者len3的計算有誤,因此slope也會不準。
?
圖23 內凹現象
為了實現一個魯棒性更好的計算方法,可以用(len2-len1)/Height*4與(len3-len1)/Height*2兩者之間更靠近tan(angle)的值作為solpe的值(在這里,angle代表的是原來RotataedRect的角度)。
多采取了一個slope備選的好處是可以避免單點的內凹或者外凸,但這仍然不是最好的解決方案。在最后的討論中會介紹一個其他的實現思路。
完成偏斜判斷與斜率計算的函數是isdeflection,下面是它的代碼。
View Code?
5.仿射變換
俗話說:行百里者半九十。前面已經做了如此多的工作,應該可以實現偏斜扭轉功能了吧?但在最后的道路中,仍然有問題等著我們。
我們已經實現了旋轉功能,并且在旋轉后的區域中截取了車牌區域,然后判斷車牌區域中的圖形是一個平行四邊形。下面要做的工作就是把平行四邊形扭正成一個矩形。
圖24 從平行四邊形車牌到矩形車牌
?
首先第一個問題就是解決如何從平行四邊形變換成一個矩形的問題。opencv提供了一個函數warpAffine,就是仿射變換函數。注意,warpAffine不僅可以讓圖像旋轉(前面介紹過),也可以進行仿射變換,真是一個多才多藝的函數。o
通過仿射變換函數可以把任意的矩形拉伸成其他的平行四邊形。opencv的官方文檔里給了一個示例,值得注意的是,這個示例演示的是把矩形變換為平行四邊形,跟我們想要的恰恰相反。但沒關系,我們先看一下它的使用方法。
?
圖25 opencv官網上對warpAffine使用的示例
?
warpAffine方法要求輸入的參數是原始圖像的左上點,右上點,左下點,以及輸出圖像的左上點,右上點,左下點。注意,必須保證這些點的對應順序,否則仿射的效果跟你預想的不一樣。通過這個方法介紹,我們可以大概看出,opencv需要的是三個點對(共六個點)的坐標,然后建立一個映射關系,通過這個映射關系將原始圖像的所有點映射到目標圖像上。
?
圖26 warpAffine需要的三個對應坐標點
?
再回來看一下我們的需求,我們的目標是把車牌區域中的平行四邊形映射為一個矩形。讓我們做個假設,如果我們選取了車牌區域中的平行四邊形車牌的三個關鍵點,然后再確定了我們希望將車牌扭正成的矩形的三個關鍵點的話,我們是否就可以實現從平行四邊形車牌到矩形車牌的扭正?
讓我們畫一幅圖像來看看這個變換的作用。有趣的是,把一個平行四邊形變換為矩形會對包圍平行四邊形車牌的區域帶來影響。
例如下圖中,藍色的實線代表扭轉前的平行四邊形車牌,虛線代表扭轉后的。黑色的實線代表矩形的車牌區域,虛線代表扭轉后的效果。可以看到,當藍色車牌被扭轉為矩形的同時,黑色車牌區域則被扭轉為平行四邊形。
注意,當車牌區域扭變為平行四邊形以后,需要顯示它的視框增大了。跟我們在旋轉圖像時碰到的情形一樣。
?
?圖27 平行四邊形的扭轉帶來的變化
?
讓我們先實際嘗試一下仿射變換吧。
根據仿射函數的需要,我們計算平行四邊形車牌的三個關鍵點坐標。其中左上點的值(xdiff,0)中的xdiff就是根據車牌區域的高度height與平行四邊形的斜率slope計算得到的:
為了計算目標矩形的三個關鍵點坐標,我們首先需要把扭轉后的原點坐標調整到平行四邊形車牌區域左上角位置。見下圖。
?
圖28 原圖像的坐標計算
依次推算關鍵點的三個坐標。它們應該是
plTri[0] = Point2f(0 + xiff, 0);plTri[1] = Point2f(width - 1, 0);plTri[2] = Point2f(0, height - 1);dstTri[0] = Point2f(xiff, 0);dstTri[1] = Point2f(width - 1, 0);dstTri[2] = Point2f(xiff, height - 1);??
根據上圖的坐標,我們開始進行一次仿射變換的嘗試。
opencv的warpAffine函數不會改變變換后圖像的大小。而我們給它傳遞的目標圖像的大小僅會決定視框的大小。不過這次我們不用擔心視框的大小,因為根據圖27看來,哪怕視框跟原始圖像一樣大,我們也足夠顯示扭正后的車牌。
看看仿射的效果。暈,好像效果不對,視框的大小是足夠了,但是圖像往右偏了一些,導致最右邊的字母沒有顯示全。
?
圖29 被偏移的車牌區域
?
這次的問題不再是目標圖像的大小問題了,而是視框的偏移問題。仔細觀察一下我們的視框,倘若我們想把車牌全部顯示的話,視框往右偏移一段距離,是不是就可以解決這個問題呢?為保證新的視框中心能夠正好與車牌的中心重合,我們可以選擇偏移xidff/2長度。正如下圖所顯示的一樣。
?
?圖30 考慮偏移的坐標計算
?
視框往右偏移的含義就是目標圖像Mat的原點往右偏移。如果原點偏移的話,那么仿射后圖像的三個關鍵點的坐標要重新計算,都需要減去xidff/2大小。
重新計算的映射點坐標為下:
plTri[0] = Point2f(0 + xiff, 0);plTri[1] = Point2f(width - 1, 0);plTri[2] = Point2f(0, height - 1);dstTri[0] = Point2f(xiff/2, 0);dstTri[1] = Point2f(width - 1 - xiff + xiff/2, 0);dstTri[2] = Point2f(xiff/2, height - 1);?
再試一次。果然,視框被調整到我們希望的地方了,我們可以看到所有的車牌區域了。這次解決的是warpAffine函數帶來的視框偏移問題。
?
圖31 完整的車牌區域
?
關于坐標調整的另一個理解就是當中心點保持不變時,平行四邊形扭正為矩形時恰好是左上的點往左偏移了xdiff/2的距離,左下的點往右偏移了xdiff/2的距離,形成一種對稱的平移。可以使用ps或者inkspace類似的矢量制圖軟件看看“斜切”的效果,
如此一來,就完成了偏斜扭正的過程。需要注意的是,向左傾斜的車牌的視框偏移方向與向右傾斜的車牌是相反的。我們可以用slope的正負來判斷車牌是左斜還是右斜。
?
6.總結
通過以上過程,我們成功的將一個偏斜的車牌經過旋轉變換等方法扭正過來。
讓我們回顧一下偏斜扭正過程。我們需要將一個偏斜的車牌扭正,為了達成這個目的我們首先需要對圖像進行旋轉。因為旋轉是個計算量很大的函數,所以我們需要考慮不再用全圖旋轉,而是區域旋轉。在旋轉過程中,會發生圖像截斷問題,所以需要使用擴大化旋轉方法。旋轉以后,只有偏斜視角的車牌才需要扭正,正視角的車牌不需要,因此還需要一個偏斜判斷過程。如此一來,偏斜扭正的過程需要旋轉,區域截取,擴大化,偏斜判斷等等過程的協助,這就是整個流程中有這么多步需要處理的原因。
下圖從另一個視角回顧了偏斜扭正的過程,主要說明了偏斜扭轉中的兩次“截取”過程。
圖32 偏斜扭正全過程
?
整個過程有一個統一的函數--deskew。下面是deskew的代碼。
View Code?
最后是改善建議:
角度偏斜判斷時可以用白色區域的輪廓來確定平行四邊形的四個點,然后用這四個點來計算斜率。這樣算出來的斜率的可能魯棒性更好。
?
三.?總結
本篇文檔介紹了顏色定位與偏斜扭轉等功能。其中顏色定位屬于作者一直想做的定位方法,而偏斜扭轉則是作者以前認為不可能解決的問題。這些問題現在都基本被攻克了,并在這篇文檔中闡述,希望這篇文檔可以幫助到讀者。
作者希望能在這片文檔中不僅傳遞知識,也傳授我在摸索過程中積累的經驗。因為光知道怎么做并不能加深對車牌識別的認識,只有經歷過失敗,了解哪些思想嘗試過,碰到了哪些問題,是如何解決的,才能幫助讀者更好地認識這個系統的內涵。
?
最后,作者很感謝能夠閱讀到這里的讀者。如果看完覺得好的話,還請輕輕點一下贊,你們的鼓勵就是作者繼續行文的動力。
?
對EasyPR做下說明:EasyPR,一個開源的中文車牌識別系統,代碼托管在github。其次,在前面的博客文章中,包含EasyPR至今的開發文檔與介紹。在后續的文章中,作者會介紹EasyPR中字符分割與識別等相關內容,歡迎繼續閱讀。
?
版權說明:
?
本文中的所有文字,圖片,代碼的版權都是屬于作者和博客園共同所有。歡迎轉載,但是務必注明作者與出處。任何未經允許的剽竊以及爬蟲抓取都屬于侵權,作者和博客園保留所有權利。
?
參考文獻:
1.http://blog.csdn.net/xiaowei_cqu/article/details/7616044
2.http://docs.opencv.org/doc/tutorials/imgproc/imgtrans/warp_affine/warp_affine.html
?
標簽: 圖像處理, 圖像識別 好文要頂 關注我 收藏該文 計算機的潛意識關注 - 11
粉絲 - 1173 榮譽:推薦博客 +加關注 80 0 ? 上一篇:小議“數據開放”
? 下一篇:EasyPR--開發詳解(7)字符分割
posted @ 2015-03-27 19:51 計算機的潛意識 閱讀(12976) 評論(24) 編輯 收藏
評論列表 #1樓 2015-03-30 09:32 sportscar 這個必須贊!!!! 支持(0)反對(0) #2樓 2015-03-30 10:13 時間乞丐 必須頂一個。 支持(0)反對(0) #3樓 2015-03-30 10:55 鍵舞飛揚 高大上呀 支持(0)反對(0) #4樓 2015-03-30 11:34 沃爾德 要不要這么屌,不明覺厲! 支持(0)反對(0) #5樓 2015-03-30 11:44 cv_ml_張欣男 雖然遠到不了商業應用的水平,但是我覺得作者可以寫書了,這是一個非常實際的項目,在一步一步操作中可以把圖像和機器學習都學習了。 支持(3)反對(0) #6樓 2015-03-30 12:40 無色 @cv_ml_張欣男
引用 雖然遠到不了商業應用的水平,但是我覺得作者可以寫書了,這是一個非常實際的項目,在一步一步操作中可以把圖像和機器學習都學習了。
主要是速度慢,商業用的都是把程序寫入芯片,窮舉類似上面的模式,用opencv移植性好些。 支持(0)反對(0) #7樓 2015-03-30 14:39 kalluwa 經過我人眼的仔細比對,圖11 偏斜扭轉效果 中的右側圖,你是從別的圖片中拉出來的。 支持(0)反對(0) #8樓 2015-03-30 14:40 xiaocong_soft 真給力 支持(0)反對(0) #9樓 2015-03-30 23:04 琢藝軒是啥請百度哈 給力!雖然暫時用不上,但相信總有一天會用到的。
琢藝軒 支持(0)反對(0) #10樓 2015-04-05 09:18 趙御辯 博主辛苦了 支持(0)反對(0) #11樓 2015-04-15 10:42 紫蔚亦人 感謝博主分享,辛苦了,加油! 支持(0)反對(0) #12樓 2015-05-06 10:34 INTme 感謝分享,字符識別和ANN的介紹什么時候能到位呢? 支持(0)反對(0) #13樓 2015-05-07 17:16 神采毅毅style 我的媽呀! 支持(0)反對(0) #14樓 2015-05-23 15:54 caisunny 感謝樓主,求字符分割和識別的介紹 支持(0)反對(0) #15樓 2015-06-03 15:45 hiCrystal 樓主辛苦了! 支持(0)反對(0) #16樓 2015-06-17 23:03 gqzhao 樓主加油,未來給個c#方案的例子。 支持(0)反對(0) #17樓 2015-06-30 10:37 小甘甘 博主微屌,期待來個MATLAB版本 支持(0)反對(0) #18樓 2016-02-22 04:47 zjyx 看得好有意思 支持(0)反對(0) #19樓 2016-08-03 19:59 咻咻咻biu 圖20旋轉后的圖形不是在放大圖像的正中央顯示是為什么? 支持(0)反對(0) #20樓 2017-01-13 16:19 天若無影 好好,認真的看了兩遍 支持(0)反對(0) #21樓 2017-03-23 16:40 FY4 大大真強!! 支持(0)反對(0) #22樓 2017-07-02 20:39 cv_ml_張欣男 我有個問題,從什么角度看車牌(矩形),才能看成平行四邊形? 支持(0)反對(0) #23樓 2017-08-23 15:02 zyz913614263 矯正,感覺可以用透視變換 支持(0)反對(0) #24樓 2017-09-27 15:47 小螞蟻要堅強哦 博主很厲害!我在運行自己的代碼時也出現了error: (-215) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in function Mat這個問題,可是不知道具體怎么改了 支持(0)反對(0) 刷新評論刷新頁面返回頂部 注冊用戶登錄后才能發表評論,請 登錄 或 注冊,訪問網站首頁。 【推薦】50萬行VC++源碼: 大型組態工控、電力仿真CAD與GIS源碼庫
【活動】騰訊云 【云+校園】套餐全新升級
【推薦】報表開發有捷徑:快速設計輕松集成,數據可視化和交互
最新IT新聞:
· Facebook正對移動端導航欄界面進行A/B測試 迄今版本多達66種
· Netflix數據:840萬人愛煲劇 24小時內看完一季劇集
· 谷歌獎勵Android應用漏洞發現者:解決惡意App問題
· 藍色起源大型火箭引擎BE-4成功完成首次點火
· 谷歌或更新Chrome瀏覽器:解決系統資源被挖礦程序濫用的問題
? 更多新聞... 最新知識庫文章:
· 實用VPC虛擬私有云設計原則
· 如何閱讀計算機科學類的書
· Google 及其云智慧
· 做到這一點,你也可以成為優秀的程序員
· 寫給立志做碼農的大學生
? 更多知識庫文章...
公告
EasyPR討論QQ群:一群:366392603,二群:583022188(已滿),三群:637614031, 加前請注明EasyPR學習討論。 昵稱:計算機的潛意識園齡:3年1個月
榮譽:推薦博客
粉絲:1173
關注:11 +加關注
| |||||||||
| 24 | 25 | 26 | 27 | 28 | 29 | 30 | |||
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |||
| 8 | 9 | 10 | 11 | 12 | 13 | 14 | |||
| 15 | 16 | 17 | 18 | 19 | 20 | 21 | |||
| 22 | 23 | 24 | 25 | 26 | 27 | 28 | |||
| 29 | 30 | 31 | 1 | 2 | 3 | 4 | |||
搜索
?隨筆檔案
- 2016年12月 (1)
- 2016年7月 (1)
- 2016年3月 (1)
- 2015年12月 (1)
- 2015年7月 (1)
- 2015年3月 (2)
- 2015年2月 (1)
- 2014年12月 (1)
- 2014年10月 (3)
- 2014年9月 (2)
積分與排名
- 積分 - 47481
- 排名 - 6951
最新評論
- 1. Re:神經網絡淺講:從神經元到深度學習
- 樓主寫得太明白了,真的很贊,我要為樓主打電話
- --missmuses
- 2. Re:神經網絡淺講:從神經元到深度學習
- 寫的太好了,語言流暢,通俗易懂,入門及佳。贊!
- --平民百姓
- 3. Re:普通程序員如何轉向AI方向
- 謝謝lz, 看了"Cocoa開發者社區"轉的你的這篇文章, 過來看原文了
- --tjipot
- 4. Re:普通程序員如何轉向AI方向
- 樓主,談戀愛造孩子的同時,也要更新博客啊。
- --燈火闌珊2oo7
- 5. Re:從機器學習談起
- 不明覺厲,大概了解,感謝
- --前程明亮
閱讀排行榜
- 1. 從機器學習談起(65076)
- 2. 神經網絡淺講:從神經元到深度學習(59020)
- 3. EasyPR--一個開源的中文車牌識別系統(57979)
- 4. EasyPR--中文開源車牌識別系統 開發詳解(1)(23828)
- 5. EasyPR--開發詳解(2)車牌定位(23796)
評論排行榜
- 1. 從機器學習談起(129)
- 2. EasyPR--一個開源的中文車牌識別系統(59)
- 3. 神經網絡淺講:從神經元到深度學習(38)
- 4. EasyPR--開發詳解(6)SVM開發詳解(36)
- 5. 小議“數據開放”(28)
總結
- 上一篇: 【python】——爬虫05 初级反爬笔
- 下一篇: 对话蔡报永:看Commvault 如何玩