小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

OPENCV條形碼檢測與識別

 行走在理想邊緣 2019-05-12

  條形碼是當(dāng)前超市和部分工廠使用比較普遍的物品,產(chǎn)品標(biāo)識技術(shù),使用攝像頭檢測一張圖片的條形碼包含有兩個步驟,第一是定位條形碼的位置,定位之后剪切出條形碼,并且識別出條形碼對應(yīng)的字符串,然后就可以調(diào)用網(wǎng)絡(luò),數(shù)據(jù)庫等手段快速進(jìn)行后續(xù)處理.

      條形碼識別要考慮到條形碼的特點,本文針對的是條形碼在圖片中的位置相對垂直,沒有各種傾斜的那種條形碼,如下圖所示

 

要定位首先要檢視這種條形碼的特點,這種圖像在X方向上的梯度肯定很明顯,同時,Y方向的梯度就沒這么明顯,所以第一步,我們應(yīng)該將圖像的灰度圖像分別計算梯度,用X方向梯度減去Y方向梯度,這樣可以保留X方向特征并且去除Y方向的干擾,處理之后圖像如下所示

可以看到,二維碼對一維碼的定位形成了干擾,但是二維碼的空間漏洞相對一維碼多很懂,于是我們考慮進(jìn)行一次模糊并且二值化,看能不能有所效果,如下(記得調(diào)整相應(yīng)的模糊化參數(shù)和閾值參數(shù),得到相對最好的結(jié)果)

有一定的效果,但是此時又出現(xiàn)問題條形碼出現(xiàn)了黑色的縫隙,不利于定位完整區(qū)域,這個時候要進(jìn)行一些形態(tài)學(xué)操作,去除黑色縫隙,我們選擇閉運算,算子根據(jù)縫隙的情況,寬度大于高度,矩形縫隙.處理以后的結(jié)果.

效果可以,又出現(xiàn)問題,二維碼的區(qū)域連著,還是面積很大,對后面我們算區(qū)域面積依然有影響,但是我們觀測二維碼的連接區(qū)域明顯要比一維碼的連接區(qū)域要細(xì)很多,也就是說,我們可以很快的腐蝕斷二維碼的連接,同時還保持一維碼的連接,然后在膨脹回來,二維碼的連接斷開就應(yīng)該不會有這個大塊的區(qū)域連著了,注意,膨脹和腐蝕的次數(shù)應(yīng)當(dāng)是一致的,保證得到結(jié)果區(qū)域的準(zhǔn)確.我選擇膨脹腐蝕四次,先膨脹斷開二維碼連接,最后的結(jié)果顯示如下

此時,二維碼的影響就基本沒有了,現(xiàn)在我們只需要先查找輪廓,然后計算圖像中每個輪廓的面積,選出面積最大的那個輪廓,計算這個輪廓的最小外包矩形,就能找到相應(yīng)的圖像區(qū)域了.這樣操作的結(jié)果和切分出來的條形碼如下所示

到目前為止,我們已經(jīng)完成了條形碼的位置定位,并且剪切出了條形碼的團,接下里對這個圖案進(jìn)行識別,識別之前,總結(jié)一下

  1. 形態(tài)學(xué)梯度運算,忽略Y方向梯度,著眼于X方向梯度
  2. 圖像模糊化,為了便于后期的圖像連接
  3. 圖像求閾值,加速算法處理,并合理使用模糊化的效果
  4. 形態(tài)學(xué)去除黑洞,閉運算
  5. 膨脹腐蝕,斷開二維碼連接
  6. 查找輪廓,計算輪廓最大面積,擬合輪廓矩形,得到最終結(jié)果

接下來條形碼識別,可以使用zbar識別庫,庫的簡介就不說了,可以自己去官網(wǎng)下載,安裝時候記得選上第三個選項,否則沒有頭文件.

安裝完成后,到安裝目錄,將bin目錄加入環(huán)境變量,在VS中VC++目錄的include中加入頭文件地址,lib地址,并加入lib名稱(連接器-輸入-附加依賴項),然后就可以使用了,具體使用查看下面的代碼,結(jié)果如下

代碼如下

復(fù)制代碼
#include <opencv2/opencv.hpp>
#include <iostream>
#include <zbar.h>

using namespace cv;
using namespace std;
using namespace zbar;

int main(int argc,char* argv[])
{
    char fileNameString[100];
    char windowNameString[50];
    char resultFileNameSring[100];
    Mat srcImage,grayImage,blurImage,thresholdImage,gradientXImage,gradientYImage,gradientImage,morphImage;
    for (int fileCount = 1;fileCount < 8;fileCount++)
    {
        sprintf(fileNameString,"F:\\opencv\\條形碼檢測與識別\\barcode_0%d.jpg",fileCount);
        sprintf(windowNameString,"result 0%d",fileCount);
        sprintf(resultFileNameSring,"F:\\opencv\\條形碼檢測與識別\\barcodeResult_0%d.jpg",fileCount);
        //讀取圖像
        srcImage = imread(fileNameString);
        if(srcImage.empty())
        {
            cout<<"image file read error"<<endl;

            return -1;
        }
        //圖像轉(zhuǎn)換為灰度圖像
        if(srcImage.channels() == 3)
        {
            cvtColor(srcImage,grayImage,CV_RGB2GRAY);
        }
        else
        {
            grayImage = srcImage.clone();
        }
        //建立圖像的梯度幅值
        Scharr(grayImage,gradientXImage,CV_32F,1,0);
        Scharr(grayImage,gradientYImage,CV_32F,0,1);
        //因為我們需要的條形碼在需要X方向水平,所以更多的關(guān)注X方向的梯度幅值,而省略掉Y方向的梯度幅值
        subtract(gradientXImage,gradientYImage,gradientImage);
        //歸一化為八位圖像
        convertScaleAbs(gradientImage,gradientImage);
        //看看得到的梯度圖像是什么樣子
        //imshow(windowNameString,gradientImage);
        //對圖片進(jìn)行相應(yīng)的模糊化,使一些噪點消除
        blur(gradientImage,blurImage,Size(9,9));
        //模糊化以后進(jìn)行閾值化,得到到對應(yīng)的黑白二值化圖像,二值化的閾值可以根據(jù)實際情況調(diào)整
        threshold(blurImage,thresholdImage,210,255,THRESH_BINARY);
        //看看二值化圖像
        //imshow(windowNameString,thresholdImage);
        //二值化以后的圖像,條形碼之間的黑白沒有連接起來,就要進(jìn)行形態(tài)學(xué)運算,消除縫隙,相當(dāng)于小型的黑洞,選擇閉運算
        //因為是長條之間的縫隙,所以需要選擇寬度大于長度
        Mat kernel = getStructuringElement(MORPH_RECT,Size(21,7));
        morphologyEx(thresholdImage,morphImage,MORPH_CLOSE,kernel);
        //看看形態(tài)學(xué)操作以后的圖像
        //imshow(windowNameString,morphImage);
        //現(xiàn)在要讓條形碼區(qū)域連接在一起,所以選擇膨脹腐蝕,而且為了保持圖形大小基本不變,應(yīng)該使用相同次數(shù)的膨脹腐蝕
        //先腐蝕,讓其他區(qū)域的亮的地方變少最好是消除,然后膨脹回來,消除干擾,迭代次數(shù)根據(jù)實際情況選擇
        erode(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(3,3)),Point(-1,-1),4);
        dilate(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(3,3)),Point(-1,-1),4);
        //看看形態(tài)學(xué)操作以后的圖像
        //imshow(windowNameString,morphImage);
        vector<vector<Point2i>>contours;
        vector<float>contourArea;
        //接下來對目標(biāo)輪廓進(jìn)行查找,目標(biāo)是為了計算圖像面積
        findContours(morphImage,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
        //計算輪廓的面積并且存放
        for(int i = 0; i < contours.size();i++)
        {
            contourArea.push_back(cv::contourArea(contours[i]));
        }
        //找出面積最大的輪廓
        double maxValue;Point maxLoc;
        minMaxLoc(contourArea, NULL,&maxValue,NULL,&maxLoc);
        //計算面積最大的輪廓的最小的外包矩形
        RotatedRect minRect = minAreaRect(contours[maxLoc.x]);
        //為了防止找錯,要檢查這個矩形的偏斜角度不能超標(biāo)
        //如果超標(biāo),那就是沒找到
        if(minRect.angle<2.0)
        {
            //找到了矩形的角度,但是這是一個旋轉(zhuǎn)矩形,所以還要重新獲得一個外包最小矩形
            Rect myRect = boundingRect(contours[maxLoc.x]);
            //把這個矩形在源圖像中畫出來
            //rectangle(srcImage,myRect,Scalar(0,255,255),3,LINE_AA);
            //看看顯示效果,找的對不對
            //imshow(windowNameString,srcImage);
            //將掃描的圖像裁剪下來,并保存為相應(yīng)的結(jié)果,保留一些X方向的邊界,所以對rect進(jìn)行一定的擴張
             myRect.x= myRect.x - (myRect.width/20);
             myRect.width = myRect.width*1.1;
            Mat resultImage = Mat(srcImage,myRect);
            if(!imwrite(resultFileNameSring,resultImage))
            {
                cout<<"file save error!"<<endl;
                return -2;
            }
        }
    }
    //檢測到了之后進(jìn)行條形碼識別
    FileStorage file("F:\\opencv\\條形碼檢測與識別\\result.xml",FileStorage::WRITE);
    for (int fileCount = 1;fileCount < 8;fileCount++)
    {
        sprintf(resultFileNameSring,"F:\\opencv\\條形碼檢測與識別\\barcodeResult_0%d.jpg",fileCount);
        sprintf(windowNameString,"result 0%d",fileCount);
        Mat result = imread(resultFileNameSring);
        if(!result.empty())
        {
            //現(xiàn)在開始識別
            cvtColor(result,grayImage,CV_RGB2GRAY);
            int width = grayImage.cols;   // extract dimensions
            int height = grayImage.rows;
            Image image(width,height,"Y800",grayImage.data,width*height);
            ImageScanner scanner;
            scanner.set_config(ZBAR_NONE,ZBAR_CFG_ENABLE,1);
            int n = scanner.scan(image);
            for (Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end();++symbol)
            {
                cout <<"pic name:\t"<<resultFileNameSring<<endl<<"code type:\t"<<symbol->get_type_name()<<endl<<                    "decode string:\t"<<symbol->get_data()<<endl;
                image.set_data(NULL,0);
                //xml文件寫入

            }
        }
    }
    waitKey(0);
    return 1;
}
復(fù)制代碼

資源如下

http://download.csdn.net/detail/dengrengong/9461797

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多