国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

一種改進(jìn)的條形碼定位方案,基于openCV實(shí)現(xiàn),附完整源代碼

這篇具有很好參考價(jià)值的文章主要介紹了一種改進(jìn)的條形碼定位方案,基于openCV實(shí)現(xiàn),附完整源代碼。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

????????全世界常用和不常用的條碼類型大概有一百多種,常用的條碼類型一般是指在世界上的多個(gè)國(guó)家或者地區(qū)使用比如EAN-13碼、UPC-A碼、Code-128碼、Code-39碼、EAN/UCC-128碼、ITF-14碼等等,而不常用的條形碼可能只是在某些國(guó)家地區(qū),或者僅在某一個(gè)行業(yè)使用,比較少見(jiàn)。

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

? ? ? ? ?根據(jù)條形碼的長(zhǎng)邊的黑條長(zhǎng)度是否一致,本文將條形碼簡(jiǎn)單的區(qū)分為等長(zhǎng)和不等長(zhǎng)兩種類型,例如Code 128每個(gè)黑條的長(zhǎng)度都是一致的,為等長(zhǎng)條形碼,而EAN - 8、EAN - 13左右兩邊的黑條比中間的多出來(lái)一截,為不等長(zhǎng)條形碼。

? ? ? ? 最近工作中的項(xiàng)目有識(shí)別條形碼的需求,在條形碼的圖片較理想時(shí),目前的開(kāi)源庫(kù)(openCV、ZXing、ZBar等)都可以滿足需求,但是實(shí)際應(yīng)用后發(fā)現(xiàn)當(dāng)條碼存在于圖片中的某個(gè)位置時(shí)所有的開(kāi)源庫(kù)都定位不到了,當(dāng)我手動(dòng)截取條形碼的ROI區(qū)域后又可以識(shí)別成功,并且openCV目前還沒(méi)有提供對(duì)code 128的支持,所以在開(kāi)發(fā)過(guò)程中,條形碼圖片預(yù)處理和定位比條形碼的識(shí)別更重要。

? ? ? ? 條形碼的定位不同于QR碼的定位,有三個(gè)回字的輪廓可以作為顯著的特征被提取,而條碼則缺少這種特征,在背景環(huán)境較復(fù)雜時(shí),很難被精準(zhǔn)的定位。在參考了網(wǎng)上很多開(kāi)源的定位方案后,發(fā)現(xiàn)很多方案都存在一定的局限性,例如被采取最多的方案是對(duì)原圖像灰度并高斯模糊處理,在x方向上對(duì)灰度圖像求取sobel邊緣并二值化,利用形態(tài)學(xué)運(yùn)算(閉運(yùn)算、膨脹等)時(shí)離散的條形輪廓合并為完整的四邊形條形碼輪廓,通過(guò)findContours函數(shù)實(shí)現(xiàn)對(duì)輪廓的提取,最后通過(guò)形態(tài)學(xué)特征(面積、慣量比、外接矩形等)篩選出條形碼實(shí)現(xiàn)定位。

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

? ? ? ? ?這種方法的局限性在于,由于只對(duì)水平方向求取邊緣,當(dāng)條形碼傾斜放置時(shí)將無(wú)法定位,又因?yàn)樾螒B(tài)學(xué)計(jì)算,當(dāng)圖像背景復(fù)雜時(shí),很容易產(chǎn)生粘連現(xiàn)象從而引入干擾,并且當(dāng)條形碼的大小、拍攝角度發(fā)生變化時(shí),相鄰的黑條之間距離也會(huì)發(fā)生變化,因此需要開(kāi)發(fā)者反復(fù)調(diào)整形態(tài)學(xué)計(jì)算的參數(shù),增加了開(kāi)發(fā)者的負(fù)擔(dān),此方法僅適用于場(chǎng)景單一的環(huán)境?;谠髡叩乃悸?,經(jīng)過(guò)三天的摸索,以code 128為例,提供一種通用性更強(qiáng)的改進(jìn)方法,并開(kāi)放完整源代碼。

????????【圖像預(yù)處理】

cv::Mat src = cv::imread("path");
cv::Mat gray,bilateral,laplacian,thresh;
cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);
cv::bilateralFilter(gray,bilateral,25,25,25);
cv::Laplacian(bilateral,laplacian,bilateral.depth(),3,3);
cv::threshold(laplacian,thresh,64,255,cv::THRESH_BINARY);

? ? ? ??對(duì)輸入圖像src灰度變換,進(jìn)行雙邊濾波在減少圖像顆粒噪聲的同時(shí)保留圖像邊緣,求取拉普拉斯導(dǎo)數(shù)得到邊緣圖像,最后通過(guò)threshold對(duì)圖像二值化,得到黑白輪廓圖像,這邊不建議使用Canny作為邊緣提取的算子,因?yàn)镃anny算子提取出的邊緣太細(xì),且當(dāng)條形碼黑條間距過(guò)小時(shí)存在粘連,不利于后續(xù)處理

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

?【輪廓提取】

for(size_t index = 0;index < contours.size();index++){
    std::vector<cv::Point> blob = contours[index];
    cv::RotatedRect minRect = cv::minAreaRect(blob);
    double area = cv::contourArea(blob);

    if(area < 20){
        continue;
    }

    float min = std::fmin(minRect.size.width,minRect.size.height);
    float max = std::fmax(minRect.size.width,minRect.size.height);
    float inertia_ratio = max/min;

    if(inertia_ratio < 5){
        continue;
    }

    if(area / double(min * max) < 0.1){
        continue;
    }

    ptSet.push_back(minRect.center);
    rectSet.push_back(minRect);
    contourSet.push_back(blob);

    cv::drawContours(contourImage,contours,int(index),cv::Scalar(0,0,255),-1);
}

????????使用findContours函數(shù)進(jìn)行輪廓提取,篩選出所有符合條件的輪廓,例如這里我們篩選掉面積不足20像素,慣量比(長(zhǎng)軸與短軸尺寸的比值)不足5,并且輪廓實(shí)際面積不及最小外接矩形面積的十分之一的輪廓,一輪篩選后,我們得到了可能滿足是條形碼的所有輪廓,并記錄下它們的中心坐標(biāo)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

??【條形碼擬合】

? ? ? ? 上一步中,得到了所有可能的條形的中心坐標(biāo),因?yàn)楸疚囊詂ode 128為例,黑條的長(zhǎng)度相等,那么所有黑條的中心點(diǎn)一定位于同一條直線上,那么如何擬合出這一條直線呢,因?yàn)槠渌蓴_項(xiàng)的存在,最小二乘法的效果往往不好,所以采用RANSAC(隨機(jī)抽樣一致算法),通過(guò)隨機(jī)抽樣擬合,多次迭代后排除掉異常點(diǎn)即得到我們想要的直線,本文不對(duì)算法做詳細(xì)的介紹,只在末尾提供源代碼。經(jīng)過(guò)擬合后根據(jù)中心點(diǎn)是否位于直線上即可得到目標(biāo)條形碼的輪廓。

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

? ? ? ? ?如圖所示,紅色輪廓為干擾項(xiàng),綠色輪廓為條形碼,圓圈為每個(gè)輪廓的中心點(diǎn),水平的黃色直線為擬合出的直線,對(duì)所有綠色的輪廓作為一個(gè)點(diǎn)集合,使用cv::minAreaRect函數(shù)得到最小外接矩形,如藍(lán)色矩形所示,垂直的黃色直線為其垂線,以這兩條直線為坐標(biāo)系,根據(jù)中學(xué)就學(xué)到的點(diǎn)與直線的關(guān)系,即可得到條形碼四個(gè)象限的各自角點(diǎn)坐標(biāo)

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

????????這個(gè)過(guò)程還會(huì)存在一個(gè)問(wèn)題:有些干擾項(xiàng)的中心點(diǎn)正好落在了直線上,因此也被擬合了進(jìn)去,針對(duì)這個(gè)現(xiàn)象采用中位數(shù)過(guò)濾方法,具體思路是根據(jù)條形碼每個(gè)黑條輪廓的長(zhǎng)軸尺寸、以及最小外接矩形的旋轉(zhuǎn)角度基本一致,可以作為一個(gè)很好的二維特征作為區(qū)分條碼和干擾項(xiàng)的依據(jù),對(duì)這個(gè)特征各自取中位數(shù),想象把他們放在一個(gè)二維坐標(biāo)系上,條碼的特征點(diǎn)是非常密集的聚集在一起的,如黃色圈內(nèi)的綠點(diǎn)所示,而干擾項(xiàng)就是離黃圈圓心(也就是中位數(shù))非常遠(yuǎn)的紅色點(diǎn),根據(jù)每個(gè)特征點(diǎn)到中位數(shù)的距離,設(shè)定一個(gè)閾值,即可排除掉所有的干擾

?【透視變換實(shí)現(xiàn)條形碼校正】

? ? ? ? 經(jīng)過(guò)了三輪篩選,我們終于得到了非常具體的條形碼位置信息,但是對(duì)于不夠成熟的開(kāi)源讀碼庫(kù)來(lái)說(shuō)還是需要做最后一步處理,也就是用過(guò)透視變換將條形碼校正,上一步中提到了我們要精準(zhǔn)的獲取條形碼各個(gè)象限的角點(diǎn)坐標(biāo),也是為了透視變換而服務(wù)的。透視變換的前后四個(gè)角點(diǎn)必須一一對(duì)應(yīng),而一旦角點(diǎn)找錯(cuò)了,進(jìn)行透視變換的時(shí)候就會(huì)出錯(cuò),由于條形碼的長(zhǎng)度、旋轉(zhuǎn)角度無(wú)法確定,故不能通過(guò)簡(jiǎn)單的方式判斷哪個(gè)點(diǎn)是左上角、哪個(gè)點(diǎn)是右下角,必須根據(jù)點(diǎn)與直線的關(guān)系來(lái)確定。為了給變換后的條形碼保留一片空白邊框區(qū)域,變換前的四個(gè)角點(diǎn)應(yīng)該往外點(diǎn),也就是等比例適當(dāng)放大其外接矩形,取放大后的角點(diǎn)。經(jīng)過(guò)測(cè)試,在不同條碼大小、拍攝角度、不同背景環(huán)境下,本方法均能夠較好的實(shí)現(xiàn)定位,本文示例圖片定位的結(jié)果如下圖所示:

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

????????解決了單一等長(zhǎng)條形碼的定位問(wèn)題,針對(duì)更加復(fù)雜的情況,例如不等長(zhǎng)條碼、或者多條形碼同時(shí)提取的問(wèn)題,暫無(wú)具體的源代碼,有需要的讀者可以嘗試下面的思路:

????????針對(duì)定位不等長(zhǎng)的條形碼,由于條形碼的不等長(zhǎng),所有中心點(diǎn)的位置無(wú)法擬合成一條完美的直線,那么數(shù)量少的長(zhǎng)邊就會(huì)被過(guò)濾掉,針對(duì)這個(gè)現(xiàn)象,可以調(diào)整RANCAC算法的參數(shù),也可以根據(jù)點(diǎn)到直線的距離公式,單獨(dú)遍歷每一個(gè)中心點(diǎn)到擬合直線的距離,再根據(jù)實(shí)際情況篩選。

opencv條形碼識(shí)別,opencv,人工智能,計(jì)算機(jī)視覺(jué)

? ? ? ? 對(duì)于多條形碼的定位,我們可以按部就班使用上面的方法,僅需要實(shí)現(xiàn)每定位一個(gè)條形碼,就把其相關(guān)的輪廓、中心點(diǎn)等信息從集合中剔除,循環(huán)以上的過(guò)程,直到圖像中再也找不到其他條形碼為止。

【完整代碼】

????????RANSAC直線擬合、中位數(shù)濾波代碼

#ifndef RANSAC_H
#define RANSAC_H

#include <opencv2/opencv.hpp>

class Filter
{
public:
    static Filter * GetInstance();
public:
    void filterLineRANSAC(std::vector<cv::Point2d> ptSet,double & a,double & b,double & c,std::vector<bool> & inlierFlag);
    void filterOutLierPointMAD(std::vector<cv::Point2d> ptSet,double threshold,std::vector<bool> & inlierFlag);
private:
    Filter() = default;
    ~Filter() = default;
private:
    //直線樣本中兩隨機(jī)點(diǎn)位置不能太近
    bool verifyComposition(const std::vector<cv::Point2d> pts);

    //得到直線擬合樣本,即在直線采樣點(diǎn)集上隨機(jī)選2個(gè)點(diǎn)
    bool getSample(std::vector<int> set, std::vector<int> &sset);

    //根據(jù)點(diǎn)集擬合直線ax+by+c=0,res為殘差
    void calcLinePara(std::vector<cv::Point2d> pts, double & a, double & b, double & c, double & res);

    //生成[0,1]之間符合高斯分布的數(shù)
    double gaussianRandom();

    //生成[0,1]之間符合均勻分布的數(shù)
    double uniformRandom();
};

#endif // RANSAC_H
#include "Filter.h"

Filter * Filter::GetInstance()
{
    static Filter obj;
    return &obj;
}

double Filter::gaussianRandom(void)
{
    static int next_gaussian = 0;
    static double saved_gaussian_value;

    double fac, rsq, v1, v2;

    if (next_gaussian == 0) {
        do {
            v1 = 2 * uniformRandom() - 1;
            v2 = 2 * uniformRandom() - 1;
            rsq = v1*v1 + v2*v2;
        } while (rsq >= 1.0 || rsq == 0.0);
        fac = sqrt(-2 * log(rsq) / rsq);
        saved_gaussian_value = v1*fac;
        next_gaussian = 1;
        return v2*fac;
    }
    else {
        next_gaussian = 0;
        return saved_gaussian_value;
    }
}

double Filter::uniformRandom(void)
{
    return double(rand()) / double(RAND_MAX);
}

void Filter::calcLinePara(std::vector<cv::Point2d> pts, double & a, double & b, double & c, double & res)
{
    res = 0;
    cv::Vec4f line;
    std::vector<cv::Point2f> ptsF;
    for (unsigned int i = 0; i < pts.size(); i++){
        ptsF.push_back(pts[i]);
    }

    fitLine(ptsF, line, cv::DIST_L2, 0, 1e-2, 1e-2);
    a = double(line[1]);
    b = double(-line[0]);
    c = double(line[0] * line[3] - line[1] * line[2]);

    for (unsigned int i = 0; i < pts.size(); i++)
    {
        double resid_ = fabs(pts[i].x * a + pts[i].y * b + c);
        res += resid_;
    }
    res /= pts.size();
}

bool Filter::getSample(std::vector<int> set, std::vector<int> &sset)
{
    int i[2];
    if (set.size() > 2)
    {
        do
        {
            for (int n = 0; n < 2; n++)
                i[n] = int(uniformRandom() * (set.size() - 1));
        } while (!(i[1] != i[0]));
        for (int n = 0; n < 2; n++)
        {
            sset.push_back(i[n]);
        }
    }
    else
    {
        return false;
    }
    return true;
}

bool Filter::verifyComposition(const std::vector<cv::Point2d> pts)
{
    cv::Point2d pt1 = pts[0];
    cv::Point2d pt2 = pts[1];
    if (abs(pt1.x - pt2.x) < 5 && abs(pt1.y - pt2.y) < 5)
        return false;

    return true;
}

void Filter::filterLineRANSAC(std::vector<cv::Point2d> ptSet, double & a, double & b, double & c, std::vector<bool> & inlierFlag)
{
    double residual_error = 2.99; //內(nèi)點(diǎn)閾值

    bool stop_loop = false;
    int maximum = 0;  //最大內(nèi)點(diǎn)數(shù)

    //最終內(nèi)點(diǎn)標(biāo)識(shí)及其殘差
    inlierFlag = std::vector<bool>(ptSet.size(), false);
    std::vector<double> resids_(ptSet.size(), 3);
    int sample_count = 0;
    int N = 500;

    double res = 0;

    // Filter
    srand(uint(time(nullptr))); //設(shè)置隨機(jī)數(shù)種子
    std::vector<int> ptsID;
    for (unsigned int i = 0; i < ptSet.size(); i++){
        ptsID.push_back(int(i));
    }
    while (N > sample_count && !stop_loop)
    {
        std::vector<bool> inlierstemp;
        std::vector<double> residualstemp;
        std::vector<int> ptss;
        int inlier_count = 0;
        if (!getSample(ptsID, ptss))
        {
            stop_loop = true;
            continue;
        }

        std::vector<cv::Point2d> pt_sam;
        pt_sam.push_back(ptSet[uint(ptss[0])]);
        pt_sam.push_back(ptSet[uint(ptss[1])]);

        if (!verifyComposition(pt_sam))
        {
            ++sample_count;
            continue;
        }

        // 計(jì)算直線方程
        calcLinePara(pt_sam, a, b, c, res);
        //內(nèi)點(diǎn)檢驗(yàn)
        for (unsigned int i = 0; i < ptSet.size(); i++)
        {
            cv::Point2d pt = ptSet[i];
            double resid_ = fabs(pt.x * a + pt.y * b + c);
            residualstemp.push_back(resid_);
            inlierstemp.push_back(false);
            if (resid_ < residual_error)
            {
                ++inlier_count;
                inlierstemp[i] = true;
            }
        }
        // 找到最佳擬合直線
        if (inlier_count >= maximum)
        {
            maximum = inlier_count;
            resids_ = residualstemp;
            inlierFlag = inlierstemp;
        }
        // 更新Filter迭代次數(shù),以及內(nèi)點(diǎn)概率
        if (inlier_count == 0)
        {
            N = 500;
        }
        else
        {
            double epsilon = 1.0 - double(inlier_count) / double(ptSet.size()); //野值點(diǎn)比例
            double p = 0.99; //所有樣本中存在1個(gè)好樣本的概率
            double s = 2.0;
            N = int(log(1.0 - p) / log(1.0 - pow((1.0 - epsilon), s)));
        }
        ++sample_count;
    }
}

void Filter::filterOutLierPointMAD(std::vector<cv::Point2d> ptSet,double threshold,std::vector<bool> & inlierFlag)
{
    double * data_x = new double[ptSet.size()];
    double * data_y = new double[ptSet.size()];

    for(size_t index = 0;index < ptSet.size();index++){
        data_x[index] = ptSet[index].x;
        data_y[index] = ptSet[index].y;
    }

    auto on_sort = [](double v1,double v2){
        return v1 > v2;
    };
    std::sort(data_x,&data_x[ptSet.size()],on_sort);
    std::sort(data_y,&data_y[ptSet.size()],on_sort);

    double median_x = (ptSet.size() % 2 == 1) ? data_x[ptSet.size() / 2] : (data_x[ptSet.size() / 2] + data_x[ptSet.size() / 2 - 1]) / 2;
    double median_y = (ptSet.size() % 2 == 1) ? data_y[ptSet.size() / 2] : (data_y[ptSet.size() / 2] + data_y[ptSet.size() / 2 - 1]) / 2;

    cv::Point2d median = cv::Point2d(median_x,median_y);
    auto distance = [](cv::Point2d p1,cv::Point2d p2) -> double{
        return std::pow(std::pow(p1.x - p2.x,2) + std::pow(p1.y - p2.y,2),0.5);
    };

    inlierFlag.clear();
    for(size_t index = 0;index < ptSet.size();index++){
        inlierFlag.push_back(distance(ptSet[index],median) < threshold);
    }

    delete[] data_x;
    delete[] data_y;
}

????????條形碼定位識(shí)別代碼,本文采用QZxing庫(kù)

QString decodeBarCode(cv::Mat & src)
{
    cv::Mat gray,bilateral,laplacian,thresh;
    cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);
    cv::bilateralFilter(gray,bilateral,25,25,25);
    cv::Laplacian(bilateral,laplacian,bilateral.depth(),3,3);
    cv::threshold(laplacian,thresh,64,255,cv::THRESH_BINARY);

    cv::imshow("gray",gray);
    cv::imshow("bilateral",bilateral);
    cv::imshow("laplacian",laplacian);
    cv::imshow("thresh",thresh);

    cv::Mat contourImage(src.rows,src.cols,CV_8UC3,cv::Scalar(0,0,0));
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    std::vector<cv::Point2d> ptSet;
    std::vector<cv::RotatedRect> rectSet;
    std::vector<std::vector<cv::Point>> contourSet;
    cv::findContours(thresh,contours,hierarchy,cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    for(size_t index = 0;index < contours.size();index++){
        std::vector<cv::Point> blob = contours[index];
        cv::RotatedRect minRect = cv::minAreaRect(blob);
        double area = cv::contourArea(blob);

        if(area < 20){
            continue;
        }

        float min = std::fmin(minRect.size.width,minRect.size.height);
        float max = std::fmax(minRect.size.width,minRect.size.height);
        float inertia_ratio = max/min;

        if(inertia_ratio < 5){
            continue;
        }

        if(area / double(min * max) < 0.1){
            continue;
        }

        ptSet.push_back(minRect.center);
        rectSet.push_back(minRect);
        contourSet.push_back(blob);

        cv::drawContours(contourImage,contours,int(index),cv::Scalar(0,0,255),-1);
    }

    if(ptSet.size() < 10){
        return QString();
    }

    double A, B, C;
    std::vector<bool> inliers_LINE;
    Filter::GetInstance()->filterLineRANSAC(ptSet, A, B, C, inliers_LINE);

    std::vector<std::vector<cv::Point>> contour_ok;
    std::vector<cv::Point2d> keypoint_set;

    for(size_t index = 0;index < inliers_LINE.size();index++){
        if(inliers_LINE[index]){
            double angle = std::min(double(rectSet[index].angle),90 - double(rectSet[index].angle));
            double length = std::max(double(rectSet[index].size.width),double(rectSet[index].size.height));

            keypoint_set.push_back(cv::Point2d(angle,length));
            contour_ok.push_back(contourSet[index]);
            cv::circle(contourImage,ptSet[index],10,cv::Scalar(255,255,255),2);
        } else {
            cv::circle(contourImage,ptSet[index],10,cv::Scalar(128,128,128),2);
        }
    }
    cv::drawContours(contourImage,contour_ok,-1,cv::Scalar(0,255,0),-1);

    std::vector<bool> inliers_POINT;
    Filter::GetInstance()->filterOutLierPointMAD(keypoint_set,10,inliers_POINT);

    std::vector<cv::Point> contours_barCode;
    for(size_t index = 0;index < keypoint_set.size();index++){
        if(inliers_POINT[index]){
            contours_barCode.insert(contours_barCode.end(), contour_ok[index].begin(), contour_ok[index].end());
        }
    }

    if(contours_barCode.empty()){
        return QString();
    }

    cv::RotatedRect barCodeRect = cv::minAreaRect(contours_barCode);

    cv::Size2f size = barCodeRect.size;
    size.width = std::min(size.width * 1.2f,size.width + 30);
    size.height = std::min(size.height * 1.2f,size.height + 30);
    barCodeRect = cv::RotatedRect(barCodeRect.center,size,barCodeRect.angle);

    drawRotatedRect(contourImage,barCodeRect,cv::Scalar(255,255,0),2);
    cv::Point2f arr[4];
    barCodeRect.points(arr);

    cv::Mat dst_warp,dst_warpRotateScale,dst_warpTransformation,dst_warpFlip;
    cv::Point2f srcPoints[4];
    cv::Point2f dstPoints[4];

    cv::Point2f center = barCodeRect.center;
    if(fabs(B) > 0){
        double v_A = B;
        double v_B = -A;
        double v_C = A * double(center.y) - B * double(center.x);

        auto h_function = [A,B,C](double x)->double{
            return -A/B*x - C/B;
        };

        auto v_function = [v_A,v_B,v_C](double y)->double{
            return - y * v_B / v_A - v_C / v_A;
        };

        for(size_t index = 0;index < 4;index++){
            double x = double(arr[index].x);
            double y = double(arr[index].y);

            if(y < h_function(x) && x > v_function(y)){
                srcPoints[0] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(255,255,128),-1);
            }

            if(y < h_function(x) && x < v_function(y)){
                srcPoints[1] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(255,128,255),-1);
            }

            if(y > h_function(x) && x < v_function(y)){
                srcPoints[2] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(255,128,128),-1);
            }

            if(y > h_function(x) && x > v_function(y)){
                srcPoints[3] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(0,128,255),-1);
            }
        }

        cv::Point2d ptStart, ptEnd;
        ptStart.x = 0;
        ptStart.y = h_function(ptStart.x);
        ptEnd.x = contourImage.cols;
        ptEnd.y = h_function(ptEnd.x);
        cv::line(contourImage, ptStart, ptEnd, cv::Scalar(0, 255, 255), 2, 8);

        ptStart.x = v_function(0);
        ptStart.y = 0;
        ptEnd.x = v_function(contourImage.rows);
        ptEnd.y = contourImage.rows;
        cv::line(contourImage, ptStart, ptEnd, cv::Scalar(0, 255, 255), 2, 8);
    } else {
        for(size_t index = 0;index < 4;index++){
            double x = double(arr[index].x);
            double y = double(arr[index].y);

            if(x > -C/A && y < double(center.y)){
                srcPoints[0] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(255,255,128),-1);
            }

            if(x < -C/A && y < double(center.y)){
                srcPoints[1] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(255,128,255),-1);
            }

            if(x < -C/A && y > double(center.y)){
                srcPoints[2] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(255,128,128),-1);
            }

            if(x > -C/A && y > double(center.y)){
                srcPoints[3] = arr[index];
                cv::circle(contourImage,arr[index],10,cv::Scalar(0,128,255),-1);
            }
            cv::circle(contourImage,arr[index],10,cv::Scalar(255,255,255),2);
        }

        cv::Point2d ptStart, ptEnd;
        ptStart.x = -C/A;
        ptStart.y = 0;
        ptEnd.x = -C/A;
        ptEnd.y = contourImage.rows;
        cv::line(contourImage, ptStart, ptEnd, cv::Scalar(0, 255, 255), 2, 8);

        ptStart.x = 0;
        ptStart.y = double(center.y);
        ptEnd.x = contourImage.cols;
        ptEnd.y = double(center.y);
        cv::line(contourImage, ptStart, ptEnd, cv::Scalar(0, 255, 255), 2, 8);
    }

    auto distance = [](cv::Point2f p1,cv::Point2f p2)->double{
        return std::pow(std::pow(p1.x - p2.x,2) + std::pow(p1.y - p2.y,2),0.5);
    };

    int width = int(distance(srcPoints[0],srcPoints[1])) + int(distance(srcPoints[2],srcPoints[3]));
    int height = int(distance(srcPoints[1],srcPoints[2])) + int(distance(srcPoints[0],srcPoints[3]));

    dstPoints[0] = cv::Point2f(width,0);
    dstPoints[1] = cv::Point2f(0,0);
    dstPoints[2] = cv::Point2f(0,height);
    dstPoints[3] = cv::Point2f(width,height);

    cv::Mat M1 = cv::getPerspectiveTransform(srcPoints, dstPoints);
    warpPerspective(src, dst_warp, M1, cv::Size(width,height));

    cv::imshow("dst_warp",dst_warp);
    cv::waitKey(0);

    QZXing qzxing;
    qzxing.setDecoder(QZXing::DecoderFormat_CODE_128);
    return qzxing.decodeImage(Mat2Image(dst_warp));
}

參考博客:opencv練習(xí)--條形碼定位識(shí)別_opencv識(shí)別條形碼位置_ZZU-Hanqi_Duan的博客-CSDN博客

RANSAC估計(jì)——以直線擬合為例_隨機(jī)采樣一致性擬合直線_Gareth Wang的博客-CSDN博客

C++去除離群值 MAD算法 - 月下叉猹 - 博客園 (cnblogs.com)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-778008.html

到了這里,關(guān)于一種改進(jìn)的條形碼定位方案,基于openCV實(shí)現(xiàn),附完整源代碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Android OpenCV(七十五): 看看剛”轉(zhuǎn)正“的條形碼識(shí)別

    Android OpenCV(七十五): 看看剛”轉(zhuǎn)正“的條形碼識(shí)別

    2021年,我們寫(xiě)過(guò)一篇《OpenCV 條碼識(shí)別 Android 平臺(tái)實(shí)踐》,當(dāng)時(shí)的條形碼識(shí)別模塊位于 opencv_contrib 倉(cāng)庫(kù),但是 OpenCV 4.8.0 版本開(kāi)始, 條形碼識(shí)別模塊已移動(dòng)到 OpenCV 主倉(cāng)庫(kù),至此我們無(wú)需自行編譯即可輕松地調(diào)用條形碼識(shí)別能力。 Bar code detector and decoder moved from Contrib to main re

    2024年02月12日
    瀏覽(44)
  • Python - OpenCV識(shí)別條形碼、二維碼(已封裝,拿來(lái)即用)

    Python - OpenCV識(shí)別條形碼、二維碼(已封裝,拿來(lái)即用)

    此代碼可識(shí)別條形碼和二維碼,已封裝好,拿來(lái)即用: 結(jié)果:

    2024年02月12日
    瀏覽(25)
  • H5實(shí)現(xiàn)掃碼讀取二維碼條形碼功能(二維碼+條形碼)

    本文主要介紹二維碼實(shí)現(xiàn)的原理 1、使用插件 npm install @zxing/library 2、主要用到 BrowserMultiFormatReader 這個(gè)構(gòu)造函數(shù),用于打開(kāi)攝像頭 視圖 核心代碼(以vue3寫(xiě)法舉例) 二維碼的樣式 ``

    2024年02月11日
    瀏覽(21)
  • Java生成條形碼

    Java生成條形碼

    生成條形碼工具類:? ?生成結(jié)果如下: ?

    2024年02月15日
    瀏覽(20)
  • uniapp生成條形碼?

    uniapp生成條形碼?

    首先先在插件市場(chǎng)找到條形碼 鏈接:https://ext.dcloud.net.cn/search?q= 下載到項(xiàng)目里面 三、在pages.json中寫(xiě)入以下: 四、在html頁(yè)面 這樣就已經(jīng)差不多了 條形碼可以出來(lái)了

    2024年02月04日
    瀏覽(18)
  • 【Java】批量生成條形碼-itextpdf

    批量生成條形碼 Controller Service

    2024年02月12日
    瀏覽(23)
  • JS 生成條形碼(一維碼)jsBarcode

    JS 生成條形碼(一維碼)jsBarcode

    script 引入 地址:https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js 也可以進(jìn)官網(wǎng)查看地址。 npm方式 安裝: 頁(yè)面引入: HTML部分加入svg容器 JS 代碼部分 三、結(jié)果 參數(shù)設(shè)置(options) option 默認(rèn)值 類型 說(shuō)明 format “auto” (CODE128) String 條形碼的類型 width 2 Number 每個(gè)條條的寬

    2024年01月20日
    瀏覽(26)
  • 【MAUI】條形碼,二維碼掃描功能

    本系列文章面向移動(dòng)開(kāi)發(fā)小白,從零開(kāi)始進(jìn)行平臺(tái)相關(guān)功能開(kāi)發(fā),演示如何參考平臺(tái)的官方文檔使用MAUI技術(shù)來(lái)開(kāi)發(fā)相應(yīng)功能。 移動(dòng)端的掃描條形碼、二維碼的功能已經(jīng)隨處可見(jiàn),已經(jīng)很難找到一個(gè)不支持掃描的App了,但是微軟的MAUI竟然沒(méi)有提供,那么我們應(yīng)該如何實(shí)現(xiàn)呢?

    2024年02月04日
    瀏覽(34)
  • 如何使用 Python 生成和讀取條形碼

    如何使用 Python 生成和讀取條形碼

    條形碼在我們的日常生活中很常見(jiàn)。只需幾個(gè)簡(jiǎn)單的步驟,您就可以使用 Python 輕松生成和掃描條形碼。 當(dāng)您從商店購(gòu)買(mǎi)商品時(shí),您所購(gòu)買(mǎi)的物品上的平行黑條紋,具有不同寬度,被稱為條形碼。條形碼是一種將數(shù)據(jù)以視覺(jué)、機(jī)器可讀的方式表示的方法。條形碼被用于存儲(chǔ)有

    2024年02月04日
    瀏覽(14)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包