最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

OpenCV實(shí)現(xiàn)車牌字符分割(C++)

 更新時(shí)間:2020年11月27日 15:47:04   作者:Steven·簡(jiǎn)談  
這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)車牌字符分割,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

之前的車牌定位中已經(jīng)獲取到了車牌的位置,并且對(duì)車牌進(jìn)行了提取。我們最終的目的是進(jìn)行車牌識(shí)別,在這之前需要將字符進(jìn)行分割,方便對(duì)每一個(gè)字符進(jìn)行識(shí)別,最后將其拼接后便是完整的車牌號(hào)碼。關(guān)于車牌定位可以看這篇文章: OpenCV車牌定位(C++) ,本文使用的圖片也是來(lái)自這里。

先來(lái)看一看原圖:

最左邊的漢字本來(lái)是 ,截取時(shí)只獲得了右邊一點(diǎn)點(diǎn)的部分,這與原圖和獲取方法都有關(guān),對(duì)于 川、滬… 這一類左右分開(kāi)的字會(huì)經(jīng)常發(fā)生這類問(wèn)題,對(duì)方法進(jìn)行優(yōu)化后可以解決,這里暫時(shí)不進(jìn)行討論。

后面的字都是完整的,字符分割的過(guò)程不會(huì)受影響。首先來(lái)一波常規(guī)操作,為了更方便處理,將其變成灰度圖片:

分割的方法不止一種,最簡(jiǎn)單的就是多加點(diǎn)人工成分,按照大致寬度再微調(diào)進(jìn)行截取,但是這樣看似最快其實(shí)成本最高,只適用于單一的圖片,因此這種容錯(cuò)低且不夠自動(dòng)的方法就不考慮了。

目前我使用了兩種不同的方法,一種是進(jìn)行邊緣檢測(cè)再檢測(cè)輪廓,根據(jù)字符的輪廓特點(diǎn)篩選出字符;另一種就是像素值判斷,主要根據(jù)像素?cái)?shù)量使用水平映射截取寬度,垂直映射因?yàn)楦叨然疽恢戮筒恍枰?,方法于水平映射一樣?/p>

兩種方法我都寫在后面,根據(jù)需要自行復(fù)制。如果要使用像素值進(jìn)行判斷的話,就需要再將灰度圖轉(zhuǎn)換成二值化圖片,使用閾值分割就行了。若使用第一種用輪廓分割的方法,灰度圖和二值化圖片都可以,結(jié)果沒(méi)什么區(qū)別。

檢測(cè)輪廓進(jìn)行分割

邊緣檢測(cè)

對(duì)圖像進(jìn)行邊緣檢測(cè),這里采用的是 Canny 邊緣檢測(cè),處理后的結(jié)果如下:

可以看到每個(gè)字的邊緣都被描繪出來(lái)了,接下來(lái)就將每個(gè)字的輪廓獲取出來(lái)。

檢測(cè)輪廓

直接使用 findContours() 將所有輪廓提取出來(lái),再將其在原圖中畫出來(lái)看看效果:

可以看到不僅僅是每個(gè)字被框出來(lái)了,還有內(nèi)部以及圖像中表現(xiàn)特殊部分的輪廓也有,接下來(lái)我們就根據(jù)每個(gè)字的大致大小篩選出我們想要的結(jié)果:

這樣看起來(lái)是不是就成功了,然后根據(jù)輪廓位置將每個(gè)字提取出來(lái)就行了,不過(guò)在這里每個(gè)輪廓的前后順序不一定是圖像中的位置,這里我使用每個(gè)輪廓左上角橫坐標(biāo) x 的大小來(lái)排序。

完整代碼:

#include <iostream> 
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <map>

using namespace std;
using namespace cv;

int main() {
 Mat img = imread("number.jpg");
 Mat gray_img;
 // 生成灰度圖像
 cvtColor(img, gray_img, CV_BGR2GRAY);
 // 高斯模糊
 Mat img_gau;
 GaussianBlur(gray_img, img_gau, Size(3, 3), 0, 0);
 // 閾值分割
 Mat img_seg;
 threshold(img_gau, img_seg, 0, 255, THRESH_BINARY + THRESH_OTSU);
 // 邊緣檢測(cè),提取輪廓
 Mat img_canny;
 Canny(img_seg, img_canny, 200, 100);
 vector<vector<Point>> contours;
 vector<Vec4i> hierarchy;
 findContours(img_canny, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point());
 int size = (int)(contours.size());
 // 保存符號(hào)邊框的序號(hào)
 vector<int> num_order;
 map<int, int> num_map;
 for (int i = 0; i < size; i++) {
 // 獲取邊框數(shù)據(jù)
 Rect number_rect = boundingRect(contours[i]);
 int width = number_rect.width;
 int height = number_rect.height;
 // 去除較小的干擾邊框,篩選出合適的區(qū)域
 if (width > img.cols/10 && height > img.rows/2) {
 rectangle(img_seg, number_rect.tl(), number_rect.br(), Scalar(255, 255, 255), 1, 1, 0);
 num_order.push_back(number_rect.x);
 num_map[number_rect.x] = i;
 }
 }
 // 按符號(hào)順序提取
 sort(num_order.begin(), num_order.end());
 for (int i = 0; i < num_order.size(); i++) {
 Rect number_rect = boundingRect(contours[num_map.find(num_order[i])->second]);
 Rect choose_rect(number_rect.x, 0, number_rect.width, gray_img.rows);
 Mat number_img = gray_img(choose_rect);
 imshow("number" + to_string(i), number_img);
 // imwrite("number" + to_string(i) + ".jpg", number_img);
 }
 imshow("添加方框", gray_img);
 waitKey(0);
 return 0;
}

像素值判斷進(jìn)行分割

分割方法:首先判斷每一列的像素值大于 0 的像素個(gè)數(shù)超過(guò)5個(gè)時(shí),認(rèn)為此列是有數(shù)字的,記錄每列像素是否大于 5,產(chǎn)生一個(gè)數(shù)組。

// 確認(rèn)為 1 的像素
 int pixrow[1000];
 for (int i = 0; i < roi_col - 1; i++) {
 for (int j = 0; j < roi_row - 1; j++) {
 pix = img_threadhold.at<uchar>(j, i);
 pixrow[i] = 0;
 if (pix > 0) {
 pixrow[i] = 1;
 break;
 }
 }
 }
 // 對(duì)數(shù)組進(jìn)行濾波,減少突變概率
 for (int i = 2; i < roi_col - 1 - 2; i++) {
 if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) >= 3) {
 pixrow[i] = 1;
 }
 else if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) <= 1) {
 pixrow[i] = 0;
 }
 }

之后記錄像素為 0 和 1 所連續(xù)的長(zhǎng)度來(lái)計(jì)算字符的寬度,最后用寬度的大小來(lái)篩選字符。

// 確認(rèn)字符位置
 int count = 0;
 bool flage = false;
 for (int i = 0; i < roi_col - 1; i++) {
 pix = pixrow[i];
 if (pix == 1 && !flage) {
 flage = true;
 position1[count] = i;
 continue;
 }
 if (pix == 0 && flage) {
 flage = false;
 position2[count] = i;
 count++;
 }
 if (i == (roi_col - 2) && flage) {
 flage = false;
 position2[count] = i;
 count++;
 }
 }

分割出的結(jié)果:

完整代碼:

#include <iostream> 
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>

using namespace std;
using namespace cv;

int main() {
 Mat img = imread("number.jpg");
 Mat gray_img;
 // 生成灰度圖像
 cvtColor(img, gray_img, CV_BGR2GRAY);
 // 高斯模糊
 Mat img_gau;
 GaussianBlur(gray_img, img_gau, Size(3, 3), 0, 0);
 // 閾值分割
 Mat img_threadhold;
 threshold(img_gau, img_threadhold, 0, 255, THRESH_BINARY + THRESH_OTSU);
 // 判斷字符水平位置
 int roi_col = img_threadhold.cols, roi_row = img_threadhold.rows, position1[50], position2[50], roi_width[50];
 uchar pix;
 // 確認(rèn)為 1 的像素
 int pixrow[1000];
 for (int i = 0; i < roi_col - 1; i++) {
 for (int j = 0; j < roi_row - 1; j++) {
 pix = img_threadhold.at<uchar>(j, i);
 pixrow[i] = 0;
 if (pix > 0) {
 pixrow[i] = 1;
 break;
 }
 }
 }
 // 對(duì)數(shù)組進(jìn)行濾波,減少突變概率
 for (int i = 2; i < roi_col - 1 - 2; i++) {
 if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) >= 3) {
 pixrow[i] = 1;
 }
 else if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) <= 1) {
 pixrow[i] = 0;
 }
 }
 // 確認(rèn)字符位置
 int count = 0;
 bool flage = false;
 for (int i = 0; i < roi_col - 1; i++) {
 pix = pixrow[i];
 if (pix == 1 && !flage) {
 flage = true;
 position1[count] = i;
 continue;
 }
 if (pix == 0 && flage) {
 flage = false;
 position2[count] = i;
 count++;
 }
 if (i == (roi_col - 2) && flage) {
 flage = false;
 position2[count] = i;
 count++;
 }
 }
 // 記錄所有字符寬度
 for (int n = 0; n < count; n++) {
 roi_width[n] = position2[n] - position1[n];
 }
 // 減去最大值、最小值,計(jì)算平均值用字符寬度來(lái)篩選
 int max = roi_width[0], max_index = 0;
 int min = roi_width[0], min_index = 0;
 for (int n = 1; n < count; n++) {
 if (max < roi_width[n]) {
 max = roi_width[n];
 max_index = n;
 }
 if (min > roi_width[n]) {
 min = roi_width[n];
 min_index = n;
 }
 }
 int index = 0;
 int new_roi_width[50];
 for (int i = 0; i < count; i++) {
 if (i == min_index || i == max_index) {}
 else {
 new_roi_width[index] = roi_width[i];
 index++;
 }
 }
 // 取后面三個(gè)值的平均值
 int avgre = (int)((new_roi_width[count - 3] + new_roi_width[count - 4] + new_roi_width[count - 5]) / 3.0);
 // 字母位置信息確認(rèn),用寬度來(lái)篩選
 int licenseX[10], licenseW[10], licenseNum = 0;
 int countX = 0;
 for (int i = 0; i < count; i++) {
 if (roi_width[i] >(avgre - 8) && roi_width[i] < (avgre + 8)) {
 licenseX[licenseNum] = position1[i];
 licenseW[licenseNum] = roi_width[i];
 licenseNum++;
 countX++;
 continue;
 }
 if (roi_width[i] > (avgre * 2 - 10) && roi_width[i] < (avgre * 2 + 10)) {
 licenseX[licenseNum] = position1[i];
 licenseW[licenseNum] = roi_width[i];
 licenseNum++;
 }
 }

 // 截取字符
 Mat number_img = Mat(Scalar(0));
 for (int i = 0; i < countX; i++) {
 Rect choose_rect(licenseX[i], 0, licenseW[i], gray_img.rows);
 number_img = gray_img(choose_rect);
 imshow("number" + to_string(i), number_img);
 // imwrite("number" + to_string(i) + ".jpg", number_img);
 }
 waitKey(0);
 return 0;
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C++算法系列之中國(guó)農(nóng)歷的算法

    C++算法系列之中國(guó)農(nóng)歷的算法

    這篇文章主要介紹了C++計(jì)算中國(guó)農(nóng)歷的深入淺析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧 
    2018-05-05
  • c語(yǔ)言顏色代碼詳解

    c語(yǔ)言顏色代碼詳解

    在本篇文章里小編給大家整理的是關(guān)于c語(yǔ)言顏色代碼的知識(shí)點(diǎn)內(nèi)容,需要的朋友們可以參考下。
    2020-02-02
  • 詳細(xì)談?wù)凜語(yǔ)言中動(dòng)態(tài)內(nèi)存

    詳細(xì)談?wù)凜語(yǔ)言中動(dòng)態(tài)內(nèi)存

    在C語(yǔ)言中,編寫程序的時(shí)候不能確定內(nèi)存的大小,希望程序在運(yùn)行的過(guò)程中根據(jù)數(shù)據(jù)量的大小動(dòng)態(tài)的分配內(nèi)存,這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中動(dòng)態(tài)內(nèi)存的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • Objective-C的內(nèi)省(Introspection)用法小結(jié)

    Objective-C的內(nèi)省(Introspection)用法小結(jié)

    這篇文章主要介紹了Objective-C的內(nèi)省(Introspection)用法,這是面向?qū)ο笳Z(yǔ)言和環(huán)境的一個(gè)強(qiáng)大特性,需要的朋友可以參考下
    2014-07-07
  • QT布局管理詳解QVBoxLayout與QHBoxLayout及QGridLayout的使用

    QT布局管理詳解QVBoxLayout與QHBoxLayout及QGridLayout的使用

    在這篇文章中,你將知道水平布局、垂直布局、網(wǎng)格布局如何輕松上手,以純代碼方式展示。對(duì)齊方式,大小設(shè)置,圖片頭像匹配標(biāo)簽,布局器里面的組件大小隨意切換大小,認(rèn)真看完這篇文章,QT布局管理器熟練使用
    2022-06-06
  • 詳解設(shè)計(jì)模式中的模板方法模式及在C++中的使用

    詳解設(shè)計(jì)模式中的模板方法模式及在C++中的使用

    這篇文章主要介紹了設(shè)計(jì)模式中的模板方法模式及在C++中的使用,模板方法將邏輯封裝到一個(gè)類中,并采取組合(委托)的方式解決這個(gè)問(wèn)題,需要的朋友可以參考下
    2016-03-03
  • c++中string類成員函數(shù)c_str()的用法

    c++中string類成員函數(shù)c_str()的用法

    c_str()函數(shù)返回一個(gè)指向正規(guī)c字符串的指針,內(nèi)容和string類的本身對(duì)象是一樣的,通過(guò)string類的c_str()函數(shù)能夠把string對(duì)象轉(zhuǎn)換成c中的字符串的樣式
    2013-09-09
  • C語(yǔ)言詳細(xì)講解strcpy strcat strcmp函數(shù)的模擬實(shí)現(xiàn)

    C語(yǔ)言詳細(xì)講解strcpy strcat strcmp函數(shù)的模擬實(shí)現(xiàn)

    這篇文章主要介紹了怎樣用C語(yǔ)言模擬實(shí)現(xiàn)strcpy與strcat和strcmp函數(shù),strcpy()函數(shù)是C語(yǔ)言中的一個(gè)復(fù)制字符串的庫(kù)函數(shù),strcat()函數(shù)的功能是實(shí)現(xiàn)字符串的拼接,strcmp()函數(shù)作用是比較字符串str1和str2是否相同
    2022-05-05
  • c語(yǔ)言網(wǎng)絡(luò)編程-標(biāo)準(zhǔn)步驟(比較簡(jiǎn)單)

    c語(yǔ)言網(wǎng)絡(luò)編程-標(biāo)準(zhǔn)步驟(比較簡(jiǎn)單)

    這篇文章主要介紹了c語(yǔ)言網(wǎng)絡(luò)編程-標(biāo)準(zhǔn)步驟(比較簡(jiǎn)單),需要的朋友可以參考下
    2014-01-01
  • C++驅(qū)動(dòng)bash的實(shí)現(xiàn)代碼

    C++驅(qū)動(dòng)bash的實(shí)現(xiàn)代碼

    這篇文章主要介紹了C++驅(qū)動(dòng)bash的實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-11-11

最新評(píng)論

海兴县| 昭觉县| 石景山区| 思茅市| 吉安市| 安庆市| 行唐县| 南充市| 凌海市| 肥西县| 新干县| 镇原县| 黔西| 巴楚县| 苏州市| 吴忠市| 吉林省| 沁水县| 原平市| 南部县| 堆龙德庆县| 旺苍县| 金昌市| 丰台区| 郎溪县| 镇巴县| 阳城县| 隆化县| 商河县| 青岛市| 崇仁县| 和硕县| 深水埗区| 涟水县| 象州县| 尉犁县| 吴旗县| 客服| 玛曲县| 甘孜| 将乐县|