Python基于OpenCV實(shí)現(xiàn)驗(yàn)證碼字符分割的方案
一、引言
在自動(dòng)化驗(yàn)證碼識(shí)別流程中,字符分割是連接圖片預(yù)處理與字符識(shí)別的關(guān)鍵環(huán)節(jié)。本文將詳細(xì)解析一段基于 OpenCV 的驗(yàn)證碼分割代碼,該代碼通過(guò)灰度處理、邊緣優(yōu)化、輪廓檢測(cè)等步驟,精準(zhǔn)提取驗(yàn)證碼中的單個(gè)字符,同時(shí)兼顧靈活性與細(xì)節(jié)保留,適用于多數(shù)簡(jiǎn)單印刷體驗(yàn)證碼場(chǎng)景。
本文用到驗(yàn)證碼的圖片

樣例:

二、核心功能拆解
該代碼的核心目標(biāo)是將一張包含多個(gè)字符的驗(yàn)證碼圖片,分割為單個(gè)字符圖片并保存。整體流程遵循 “預(yù)處理→特征提取→篩選→分割” 的技術(shù)路徑,具體包含 6 個(gè)關(guān)鍵步驟:
- 圖片讀取與灰度轉(zhuǎn)換
- 邊緣噪聲優(yōu)化處理
- 中值濾波去噪
- 二值化突出字符輪廓
- 輪廓檢測(cè)與有效區(qū)域篩選
- 按順序分割并保存字符
三、代碼逐段深度解析
1. 函數(shù)入口與圖片讀取
def split_picture(imagepath):
# 讀取圖片:以灰度模式(0)讀取,減少色彩干擾
gray = cv2.imread(imagepath, 0)
if gray is None:
print(f"錯(cuò)誤:無(wú)法讀取圖片,請(qǐng)檢查路徑 '{imagepath}' 是否正確")
return- 核心作用:作為函數(shù)入口,接收?qǐng)D片路徑并完成初始化讀取。
- 關(guān)鍵細(xì)節(jié):
- 使用
cv2.imread(imagepath, 0)以灰度模式讀取圖片,相比彩色模式(默認(rèn) 1),可減少通道數(shù),降低后續(xù)計(jì)算復(fù)雜度。 - 增加圖片讀取校驗(yàn)(
gray is None),當(dāng)路徑錯(cuò)誤或文件損壞時(shí),直接提示錯(cuò)誤并退出,避免后續(xù)代碼報(bào)錯(cuò)。
- 使用
2. 邊緣噪聲優(yōu)化
# 邊緣處理:僅輕微處理邊緣(避免過(guò)度裁剪字符)
height, width = gray.shape
# 只處理最外層1像素,避免大面積修改邊緣導(dǎo)致字符丟失
for i in range(width):
gray[0, i] = 255
gray[height - 1, i] = 255
for j in range(height):
gray[j, 0] = 255
gray[j, width - 1] = 255- 核心作用:消除圖片邊緣的少量噪聲(如黑邊、雜點(diǎn)),同時(shí)避免破壞邊緣字符。
- 設(shè)計(jì)思路:
- 僅將圖片最外層 1 像素設(shè)為白色(255,對(duì)應(yīng)背景色),而非大面積裁剪邊緣。
- 避免因邊緣裁剪導(dǎo)致字符不完整(如驗(yàn)證碼字符緊貼圖片邊緣的場(chǎng)景)。
3. 中值濾波去噪
# 中值濾波:減小模板(避免模糊字符細(xì)節(jié)) blur = cv2.medianBlur(gray, 3) # 3*3模板,保留更多字符細(xì)節(jié)
- 核心作用:去除圖片中的椒鹽噪聲(如驗(yàn)證碼中的隨機(jī)黑點(diǎn)),同時(shí)保留字符邊緣細(xì)節(jié)。
- 參數(shù)選擇邏輯:
- 選用
3*3濾波模板(第二個(gè)參數(shù)),相比5*5或更大模板,能最大限度減少字符模糊(如 “1”“7” 等細(xì)線條字符不會(huì)被濾除)。 - 中值濾波對(duì)椒鹽噪聲的抑制效果優(yōu)于均值濾波,更適合驗(yàn)證碼這類高對(duì)比度圖片。
- 選用
4. 二值化處理
# 二值化:降低閾值(確保字符被正確識(shí)別為黑色) # 嘗試更低的閾值(150),避免字符因亮度問(wèn)題被誤判為背景 ret, thresh1 = cv2.threshold(blur, 150, 255, cv2.THRESH_BINARY)
- 核心作用:將灰度圖轉(zhuǎn)換為黑白二值圖,使字符(黑色)與背景(白色)完全分離,為后續(xù)輪廓檢測(cè)做準(zhǔn)備。
- 關(guān)鍵參數(shù)解析:
閾值150:低于 150 的像素設(shè)為 0(黑色,字符),高于 150 的設(shè)為 255(白色,背景)。選擇較低閾值是為了應(yīng)對(duì)亮度不均的驗(yàn)證碼(如字符偏灰的情況),避免字符被誤判為背景。cv2.THRESH_BINARY:基礎(chǔ)二值化模式,直接區(qū)分黑白,適合對(duì)比度明顯的驗(yàn)證碼。
5. 輪廓檢測(cè)與信息打印
# 查找輪廓:保留所有輪廓(包括內(nèi)部結(jié)構(gòu),避免漏檢)
contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 打印所有輪廓信息(方便調(diào)試)
print("所有輪廓信息(x, y, 寬, 高, 面積):")
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
area = w * h
print(f"輪廓:x={x}, y={y}, 寬={w}, 高={h}, 面積={area}, 寬高比={w / h:.2f}")- 核心作用:檢測(cè)二值圖中所有閉合區(qū)域(輪廓),并打印輪廓細(xì)節(jié)用于調(diào)試。
- 關(guān)鍵設(shè)置:
cv2.RETR_LIST:提取所有輪廓,不建立輪廓間的層級(jí)關(guān)系(如字符內(nèi)部的孔洞輪廓也會(huì)被保留),避免漏檢復(fù)雜字符(如 “0”“8”)。- 打印輪廓的
x/y坐標(biāo)(左上角)、寬高、面積和寬高比,方便用戶根據(jù)實(shí)際輸出調(diào)整后續(xù)篩選條件(如某類驗(yàn)證碼字符面積普遍為 500-1000,可據(jù)此優(yōu)化面積閾值)。
6. 有效輪廓篩選
# 大幅放寬過(guò)濾條件(優(yōu)先確保目標(biāo)輪廓被保留)
valid_contours = []
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
area = w * h
# 邊緣排除:幾乎不排除(只排除緊貼邊緣的1像素)
if x < 1 or y < 1 or x + w > width - 1 or y + h > height - 1:
continue
# 面積范圍:覆蓋之前的大輪廓(上限提高到40000,下限200)
if area < 200 or area > 40000:
continue
# 寬高比:允許更寬的字符(如M)
if w / h < 0.3 or w / h > 3:
continue
valid_contours.append((x, y, w, h))- 核心作用:從所有輪廓中篩選出 “符合字符特征” 的區(qū)域,排除噪聲輪廓(如小雜點(diǎn)、大面積背景塊)。
- 篩選條件設(shè)計(jì):
- 邊緣排除:僅排除緊貼圖片邊緣 1 像素的輪廓,避免誤刪邊緣字符。
- 面積篩選:保留面積 200-40000 的輪廓,覆蓋多數(shù)驗(yàn)證碼字符大小(小到 “1”,大到 “W”)。
- 寬高比篩選:保留寬高比 0.3-3 的輪廓,既排除過(guò)細(xì)的噪聲(如寬高比 <0.3 的細(xì)線),也排除過(guò)寬的背景塊(如寬高比> 3 的長(zhǎng)條),同時(shí)兼容 “M”“W” 等寬字符。
7. 排序與分割保存
# 按x坐標(biāo)排序(確保字符順序與驗(yàn)證碼一致)
valid_contours.sort(key=lambda c: c[0])
# 輸出結(jié)果并保存
if not valid_contours:
print("\n未找到有效字符區(qū)域!請(qǐng)根據(jù)上面的輪廓信息進(jìn)一步放寬條件。")
else:
print(f"\n共找到 {len(valid_contours)} 個(gè)有效區(qū)域:")
for i, (x, y, w, h) in enumerate(valid_contours, 1):
print(f"有效區(qū)域 {i}:(x={x}, y={y}, 寬={w}, 高={h})")
# 裁剪并保存單個(gè)字符
cv2.imwrite(f'char{i}.jpg', thresh1[y:y + h, x:x + w])- 核心作用:按字符在驗(yàn)證碼中的橫向順序排序,并將每個(gè)有效區(qū)域裁剪為單獨(dú)圖片保存。
- 關(guān)鍵邏輯:
- 按
x坐標(biāo)排序:驗(yàn)證碼字符通常橫向排列,x 坐標(biāo)越小越靠左,排序后保存的圖片(char1.jpg、char2.jpg)與實(shí)際字符順序一致,方便后續(xù)識(shí)別。 - 保存路徑:以
char+序號(hào).jpg命名(如 char1.jpg),默認(rèn)保存在代碼運(yùn)行目錄,便于批量處理。
- 按
8. 主函數(shù)調(diào)用
def main():
imagepath = 'Verification_Code.png'
split_picture(imagepath)
if __name__ == "__main__":
main()- 核心作用:定義默認(rèn)驗(yàn)證碼路徑,作為代碼入口,直接運(yùn)行即可執(zhí)行分割流程。
- 使用提示:用戶只需將
imagepath改為實(shí)際驗(yàn)證碼圖片路徑(如'D:/test/yzm.png'),即可快速測(cè)試。
四、關(guān)鍵優(yōu)化點(diǎn)總結(jié)
相比傳統(tǒng)驗(yàn)證碼分割代碼,該實(shí)現(xiàn)的核心優(yōu)勢(shì)在于 “保留細(xì)節(jié) + 靈活可調(diào)”,具體優(yōu)化點(diǎn)如下:
- 最小化邊緣處理:僅處理最外層 1 像素,避免字符裁剪,適配邊緣字符場(chǎng)景。
- 小模板濾波:3*3 中值濾波在去噪的同時(shí),最大程度保留字符細(xì)節(jié)(如細(xì)線條、拐角)。
- 低閾值二值化:150 的閾值適配亮度不均的驗(yàn)證碼,減少字符誤判。
- 寬松篩選條件:大面積范圍(200-40000)和寬高比(0.3-3),兼容多數(shù)驗(yàn)證碼類型,降低用戶調(diào)試成本。
- 調(diào)試友好:打印所有輪廓信息,方便用戶根據(jù)實(shí)際場(chǎng)景調(diào)整參數(shù)(如某類驗(yàn)證碼字符面積偏小,可降低面積下限)。
五、使用說(shuō)明與調(diào)試建議
1. 基礎(chǔ)使用步驟
- 安裝 OpenCV 庫(kù):執(zhí)行
pip install opencv-python。 - 將驗(yàn)證碼圖片命名為
Verification_Code.png,與代碼放在同一目錄;或修改main()中的imagepath為實(shí)際路徑。 - 運(yùn)行代碼,分割后的字符圖片會(huì)以
char1.jpg、char2.jpg等名稱保存在當(dāng)前目錄。
2. 常見問(wèn)題調(diào)試
- 問(wèn)題 1:未找到有效字符區(qū)域解決方案:查看 “所有輪廓信息”,若目標(biāo)字符輪廓存在但被篩選掉,可調(diào)整
valid_contours中的條件(如降低面積下限、放寬寬高比)。 - 問(wèn)題 2:分割出多余噪聲塊解決方案:若存在小噪聲塊(面積 < 200),可適當(dāng)提高面積下限(如改為 300);若存在寬高比異常的塊,可調(diào)整寬高比范圍(如改為 0.5-2)。
- 問(wèn)題 3:字符順序錯(cuò)亂解決方案:確認(rèn)驗(yàn)證碼為橫向排列(若為縱向排列,需將排序鍵改為
lambda c: c[1],按 y 坐標(biāo)排序)。
六、完整Python代碼展示
import cv2
def split_picture(imagepath):
# 讀取圖片
gray = cv2.imread(imagepath, 0)
if gray is None:
print(f"錯(cuò)誤:無(wú)法讀取圖片,請(qǐng)檢查路徑 '{imagepath}' 是否正確")
return
# 邊緣處理:僅輕微處理邊緣(避免過(guò)度裁剪字符)
height, width = gray.shape
# 只處理最外層1像素,避免大面積修改邊緣導(dǎo)致字符丟失
for i in range(width):
gray[0, i] = 255
gray[height - 1, i] = 255
for j in range(height):
gray[j, 0] = 255
gray[j, width - 1] = 255
# 中值濾波:減小模板(避免模糊字符細(xì)節(jié))
blur = cv2.medianBlur(gray, 3) # 3*3模板,保留更多字符細(xì)節(jié)
# 二值化:降低閾值(確保字符被正確識(shí)別為黑色)
# 嘗試更低的閾值(150),避免字符因亮度問(wèn)題被誤判為背景
ret, thresh1 = cv2.threshold(blur, 150, 255, cv2.THRESH_BINARY)
# 查找輪廓:保留所有輪廓(包括內(nèi)部結(jié)構(gòu),避免漏檢)
contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 打印所有輪廓信息(方便調(diào)試)
print("所有輪廓信息(x, y, 寬, 高, 面積):")
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
area = w * h
print(f"輪廓:x={x}, y={y}, 寬={w}, 高={h}, 面積={area}, 寬高比={w / h:.2f}")
# 大幅放寬過(guò)濾條件(優(yōu)先確保目標(biāo)輪廓被保留)
valid_contours = []
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
area = w * h
# 邊緣排除:幾乎不排除(只排除緊貼邊緣的1像素)
if x < 1 or y < 1 or x + w > width - 1 or y + h > height - 1:
continue
# 面積范圍:覆蓋之前的大輪廓(上限提高到40000,下限200)
if area < 200 or area > 40000:
continue
# 寬高比:允許更寬的字符(如M)
if w / h < 0.3 or w / h > 3:
continue
valid_contours.append((x, y, w, h))
# 按x坐標(biāo)排序
valid_contours.sort(key=lambda c: c[0])
# 輸出結(jié)果
if not valid_contours:
print("\n未找到有效字符區(qū)域!請(qǐng)根據(jù)上面的輪廓信息進(jìn)一步放寬條件。")
else:
print(f"\n共找到 {len(valid_contours)} 個(gè)有效區(qū)域:")
for i, (x, y, w, h) in enumerate(valid_contours, 1):
print(f"有效區(qū)域 {i}:(x={x}, y={y}, 寬={w}, 高={h})")
cv2.imwrite(f'char{i}.jpg', thresh1[y:y + h, x:x + w])
def main():
imagepath = 'Verification_Code.png'
split_picture(imagepath)
if __name__ == "__main__":
main()程序運(yùn)行截圖如下:





七、總結(jié)
本文詳細(xì)解析了一個(gè)基于OpenCV的驗(yàn)證碼字符分割方案。該方案通過(guò)灰度轉(zhuǎn)換、邊緣處理、中值濾波、二值化、輪廓檢測(cè)和篩選等步驟,實(shí)現(xiàn)驗(yàn)證碼字符的精準(zhǔn)分割。核心特點(diǎn)包括:僅處理1像素邊緣保護(hù)字符完整性、3×3中值濾波保留細(xì)節(jié)、150低閾值二值化適應(yīng)亮度不均、寬松篩選條件(面積200-40000,寬高比0.3-3)兼容多種字符類型。代碼提供調(diào)試信息輸出功能,便于參數(shù)調(diào)整,適用于多數(shù)簡(jiǎn)單驗(yàn)證碼場(chǎng)景,最終按x坐標(biāo)排序輸出分割后的字符圖片。
以上就是Python基于OpenCV實(shí)現(xiàn)驗(yàn)證碼字符分割的方案的詳細(xì)內(nèi)容,更多關(guān)于Python OpenCV驗(yàn)證碼字符分割的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
pandas string轉(zhuǎn)dataframe的方法
下面小編就為大家分享一篇pandas string轉(zhuǎn)dataframe的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Python加載數(shù)據(jù)的5種不同方式(收藏)
這篇文章主要介紹了Python加載數(shù)據(jù)的5種不同方式(收藏),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
解決jupyter notebook啟動(dòng)后沒(méi)有token的坑
這篇文章主要介紹了解決jupyter notebook啟動(dòng)后沒(méi)有token的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
Python抓取聚劃算商品分析頁(yè)面獲取商品信息并以XML格式保存到本地
這篇文章主要為大家詳細(xì)介紹了Python抓取聚劃算商品分析頁(yè)面獲取商品信息,并以XML格式保存到本地的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Python接口自動(dòng)化淺析登錄接口測(cè)試實(shí)戰(zhàn)
本文主要接好了python接口自動(dòng)化的接口概念、接口用例設(shè)計(jì)及登錄,跟隨本文章來(lái)進(jìn)行一個(gè)接口用例設(shè)計(jì)及登錄接口測(cè)試實(shí)戰(zhàn),有需要的朋友可以參考下2021-08-08
Python 轉(zhuǎn)換時(shí)間戳為指定格式日期
這篇文章主要為大家介紹了Python轉(zhuǎn)換時(shí)間戳,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-12-12
Python學(xué)習(xí)小技巧之列表項(xiàng)的推導(dǎo)式與過(guò)濾操作
這篇文章主要給大家介紹了Python學(xué)習(xí)小技巧之列表項(xiàng)的推導(dǎo)式與過(guò)濾操作的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看把。2017-05-05

