生活随笔
收集整理的這篇文章主要介紹了
基于HALCON的喷码字符自训练与识别
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
前言
最近視覺項目需求,建立10個及以上的字符模板庫,然后進行生產(chǎn)時,提取出噴碼區(qū)域與模板進行比對得到對應的分數(shù),以分數(shù)的高低來判斷噴碼的好壞。 如圖:噴碼樣品 根據(jù)需求,可以借鑒Halcon中訓練字符的思路來解決此問題。 主要流程:摳圖–>訓練–>識別
第一步——訓練文件建立
首先用戶輸入檢測噴碼的字符,在對應位置會自動生成以噴碼為名字的文件夾,然后會對應生成單個字符的子文件夾(主要用于存放單個字符圖片)。 算法邏輯:
這里我們來看看具體的Halcon源碼(僅供參考)
Code
: = [ '6' , '2' ]
filenum
: = | Code
|
string
: = [ '' ]
Color
: = 1 gen_rectangle1 ( Rectangle
, 2817.87 , 562.239 , 7395.57 , 4431.49 )
* 開始摳圖
FilePath
: = 'E:/OCR-test/10模板噴碼測試/9815B'
list_files ( FilePath
, [ 'files' , 'follow_links' ] , ImageFiles
)
tuple_regexp_select ( ImageFiles
, [ '\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$' , 'ignore_case' ] , ImageFiles
)
parse_filename ( ImageFiles
, BaseName
, Extension
, Directory
)
* 根據(jù)客戶輸入字符自動生成文件夾名字
for x
: = 0 to filenum
- 1 by
1 string
[ 0 ] : = string
[ 0 ] + Code
[ x
]
endfor
Path
: = 'E:/OCR-test/' + string
+ '/' * 判斷是否有主文件夾,沒有則創(chuàng)建主文件夾
file_exists ( 'E:/OCR-test/' + string
, FileExists
)
if ( FileExists
)
else make_dir ( 'E:/OCR-test/' + string
)
endif
* 判斷是否有字符子文件夾沒有就創(chuàng)建子文件夾
for j
: = 0 to filenum
- 1 by
1 file_exists ( Path
+ Code
[ j
] , FileNum
) if ( FileNum
) else make_dir ( Path
+ Code
[ j
] ) endif
endfor
stop ( )
這里是HALCON內(nèi)部的實現(xiàn),可以在手動輸入字符自動生成文件夾。
第二步——產(chǎn)品轉(zhuǎn)正
產(chǎn)品的圖片的校正,利用仿射變化來將圖像進行平移旋轉(zhuǎn): 通常我們Halcon中計算出的是弧度,這里我們需要了解符號代表的意義**(順時針方向旋轉(zhuǎn)的是負號,逆時針方向旋轉(zhuǎn)的是正號)** 效果如下: 轉(zhuǎn)正前: 轉(zhuǎn)正后: 具體的代碼實現(xiàn):
read_image ( Image
, ImageFiles
[ Index
] ) rgb1_to_gray ( Image
, GrayImage
) threshold ( GrayImage
, Region
, 10 , 170 ) connection ( Region
, RegionTemp
) select_shape_std ( RegionTemp
, RegionTemp
, 'max_area' , 70 ) orientation_region ( RegionTemp
, Phi
) area_center ( RegionTemp
, Area
, Row
, Column
) if ( Phi
< 0 ) AimPhi
: = rad ( - 90 ) else AimPhi
: = rad ( 90 ) endif
vector_angle_to_rigid ( Row
, Column
, Phi
, Row
, Column
, AimPhi
, HomMat2D
) affine_trans_image ( Image
, ImageAffineTrans
, HomMat2D
, 'constant' , 'false' )
vector_angle_to_rigid (Row, Column, Phi, Row1, Column1, AimPhi, HomMat2D) **Row:**變化前原點列坐標(這里是產(chǎn)品的中點) **Column:**變化前原點行坐標(這里是產(chǎn)品的中點) **Phi:**變化前的原點角度 **Row1:**變化后原點列坐標 **Column1:**變化后原點行坐標 **AimPhi:**變化的原點角度 **HomMat2D:**生成的旋轉(zhuǎn)+平移矩陣
第三步——字符摳圖
現(xiàn)在需要將我們的噴碼字符利用圖像分割技術(shù)將其提取出來 首先將圖像通道分割下(這里是彩色圖片,黑白圖片可以直接進行提取) 此處以藍色噴碼為例: 使用通道相減突出藍色區(qū)域,然后做簡單的閾值分割
sub_image ( R
, G
, ImageTemp
, 1 , 0 ) threshold ( ImageTemp
, RegionRoi
, 14 , 255 ) opening_rectangle1 ( RegionRoi
, RegionOpen
, 5 , 5 ) reduce_domain ( ImageTemp
, RegionOpen
, ImageReduced
) crop_domain ( ImageReduced
, ImagePart1
)
如果有的噴碼噴印傾斜,需要校正。(原因:因為需要利用形態(tài)學將噴碼連接為一個圖像對象,防止噴碼粘連)
* 矯正傾斜噴碼
text_line_slant ( ImagePart1
, ImagePart1
, 150 , - rad ( 45 ) , rad ( 45 ) , SlantAngle
) hom_mat2d_identity ( HomMat2DIdentity
) hom_mat2d_slant ( HomMat2DIdentity
, - SlantAngle
, 'x' , 0 , 0 , HomMat2DSlant
) affine_trans_image ( ImagePart1
, AffImagePart1
, HomMat2DSlant
, 'nearest_neighbor' , 'true' )
傾斜噴碼: 傾斜校正: 接著,將需要的噴碼提取出來進行***歸一化操作***(這步很重要!!!)就是將摳取出的噴碼圖片為一張二值圖像(只有黑和白)。
threshold ( AffImagePart1
, RegionRoi
, 14 , 255 ) opening_rectangle1 ( RegionRoi
, RegionOpen
, 10 , 5 ) dilation_rectangle1 ( RegionOpen
, RegionTemp
, 1 , 200 ) dilation_rectangle1 ( RegionTemp
, RegionTemp
, 10 , 1 ) connection ( RegionTemp
, RegionTemp
) intersection ( RegionTemp
, RegionOpen
, RegionInter
) select_shape ( RegionInter
, RegionTemp
, [ 'height' , 'area' , 'width' ] , \
'and' , [ 250 , 3000 , 90 ] , [ 500 , 50000 , 300 ] ) count_obj ( RegionTemp
, Number
) sort_region ( RegionTemp
, RegionTemp
, 'character' , 'true' , 'row' ) for RedIndex
: = 1 to Number by
1 select_obj ( RegionTemp
, ObjectRegionTemp
, RedIndex
) smallest_rectangle2 ( ObjectRegionTemp
, Row4
, Column4
, Phi2
, Length11
, Length21
) gen_rectangle2 ( Rectangle3
, Row4
, Column4
, Phi2
, Length11
, Length21
) reduce_domain ( AffImagePart1
, Rectangle3
, ImageReduced1
) crop_domain ( ImageReduced1
, ImagePart
) binary_threshold ( ImagePart
, RegionCode
, 'max_separability' , 'light' , UsedThreshold1
) opening_rectangle1 ( RegionCode
, RegionCode
, 10 , 5 ) get_image_size ( ImagePart
, Width
, Height
) gen_image_const ( ImageBk
, 'byte' , Width
, Height
) overpaint_region ( ImageBk
, ImageBk
, 255 , 'fill' ) overpaint_region ( ImageBk
, RegionCode
, 0 , 'fill' ) tuple_rand ( 1 , Rand
) Index_rand
: = int ( Rand
* 1000 ) write_image ( ImageBk
, 'jpeg' , 0 , Path
+ Code
[ RedIndex
- 1 ] + '/' + Code
[ RedIndex
- 1 ] + '_' + Index_rand
)
第四步——字符訓練
字符的圖片現(xiàn)在有了,接下來就可以通過簡單的二值閾值分割得到字符區(qū)域: 字符摳圖結(jié)果: 算法邏輯: 首先我們需要建立一個“字符集文件”字符集文件的格式為“.trf” 創(chuàng)建一個Halcon中定義的一個錯誤處理來規(guī)避重復創(chuàng)建字符集文件,默認下一次的創(chuàng)建會復寫字符集。
TrainFile1
: = string
+ '-0-9A-Z.trf'
dev_set_check ( '~give_error' )
delete_file ( TrainFile1
)
dev_set_check ( 'give_error' )
將字符寫入字符集: 此處兩個for循壞,是實現(xiàn)自動將字符寫入對應子文件
for FileIndex
: = 0 to filenum
- 1 by
1 list_files ( Path
+ Code
[ FileIndex
] , [ 'files' , 'follow_links' ] , ImageFiles
) tuple_regexp_select ( ImageFiles
, [ '\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$' , 'ignore_case' ] , ImageFiles
) for Index
: = 0 to
| ImageFiles
| - 1 by
1 read_image ( Image
, ImageFiles
[ Index
] ) binary_threshold ( Image
, Region
, 'max_separability' , 'dark' , UsedThreshold
) opening_rectangle1 ( Region
, RegionOpen
, 5 , 5 ) append_ocr_trainf ( RegionOpen
, GrayImage
, Code
[ FileIndex
] , TrainFile1
) endfor
endfor
stop ( )
字符集的創(chuàng)建完畢后我們開始自己訓練我們的分類器(MLP)文件格式為“.omc” 其中uniq(sort(Code)),表示分類的類別不能有重復的,這個是合并重復的標簽。 例如:標簽中a[‘0’,‘0’,‘0’,‘2’,‘1’]使用了后會變?yōu)閍[‘0’,‘1’,‘2’]
read_ocr_trainf_names ( TrainFile1
, CharacterNames
, CharacterCount
)
create_ocr_class_mlp ( 8 , 10 , 'constant' , 'default' , uniq ( sort ( Code
) ) , 80 , 'none' , 10 , 42 , OCRHandle
)
trainf_ocr_class_mlp ( OCRHandle
, TrainFile1
, 200 , 1 , 0.01 , Error
, ErrorLog
)
FontFile
: = 'shanjin.omc'
write_ocr_class_mlp ( OCRHandle
, FontFile
)
clear_ocr_class_mlp ( OCRHandle
)
stop ( )
第五步—— 字符檢測識別
最后進行我們字符的識別,同樣的將字符提取出來,讀取我們自己的分類器(MLP)進行OCR識別 算法邏輯: 這里我們來看看具體的源碼(僅供參考)
list_files ( FilePath
, [ 'files' , 'follow_links' ] , ImageFiles
)
tuple_regexp_select ( ImageFiles
, [ '\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$' , 'ignore_case' ] , ImageFiles
)
for Index
: = 0 to
| ImageFiles
| - 1 by
1 read_image ( Image
, ImageFiles
[ Index
] ) rgb1_to_gray ( Image
, GrayImage
) Locate ( Image
, ImageTemp
, ProductRegion
, [ ] ) CodeDiff
: = 31 Params
: = [ Color
, CodeDiff
] ExtracteRegionOCV ( ImageTemp
, Rectangle
, ProductRegion
, RegionResult
, Params
, NumberDots
, NumberAllDots
) stop ( ) fill_up_shape ( RegionResult
, RegionFillUp
, 'area' , 1 , 100 ) sort_region ( RegionFillUp
, SortedRegions
, 'character' , 'true' , 'row' ) union1 ( SortedRegions
, RegionUnion
) difference ( ImageTemp
, RegionUnion
, RegionDifference
) paint_region ( RegionDifference
, GrayImage
, ImageOcrRaw
, 255 , 'fill' ) paint_region ( RegionUnion
, ImageOcrRaw
, ImageOcr
, 0 , 'fill' ) read_ocr_class_mlp ( FontFile
, OCRHandle
) do_ocr_multi_class_mlp ( SortedRegions
, ImageOcr
, OCRHandle
, Class
, Confidence
) stop ( )
endfor
最終結(jié)果:
寫在最后
在我們訓練完畢MLP后我們想要測試他的能力怎么辦呢?我們可以使用助手的功能: 在調(diào)試自己訓練的分類器是否正確的時候可以打開Halcon自帶的OCR助手來查看自己的分類器能力。 在其中加載自己的字符集和分類器 這里我們還可以點擊編輯菜單中選擇"生成變化"這里可以我們的字符集變得更豐富: 然后用分類器(MLP)測試下識別的能力:
總結(jié)
以上是生活随笔 為你收集整理的基于HALCON的喷码字符自训练与识别 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。