1. 程式人生 > >VC++ 利用Opencv 做的一個發票識別程式,識別有誤

VC++ 利用Opencv 做的一個發票識別程式,識別有誤

如題,如圖

上圖為識別有誤樣張,下圖為識別正常樣張。

現在是簡單的貼票識別沒問題,但是較複雜的貼票就會識別有誤,識別不全,請教大家誰能幫我看下原因?

程式碼段:
void do_bill_image(const char* pTifFile)
{
    if (NULL == pTifFile)
        return;

    int p[3];
    p[0] = CV_IMWRITE_JPEG_QUALITY;
    p[1] = 85;
    p[2] = 0;


    IplImage* lpImgSrc = cvLoadImage(pTifFile, CV_LOAD_IMAGE_COLOR);*/
    if (lpImgSrc)
    {
        std::map<int, RECT> mapRC;
        mapRC.clear();

        IplImage* lpImgBinary = get_image_binary(lpImgSrc, 0);
        if (lpImgBinary)
        {
            removice_noise_image(lpImgBinary, 3);//降噪

            cvErode(lpImgBinary, lpImgBinary, NULL, 3);//腐蝕
            //cvDilate(lpImgBinary, lpImgBinary, NULL, 3);//膨脹
            
            removeblack(lpImgBinary);//漫水填充

            int iSpace = lpImgBinary->width/2;
            std::vector<UINT> vecHistogramSum;
            vecHistogramSum.clear();

            {    // 圖片畫素統計(垂直投影)
                IplImage* lpImageOut = cvCreateImage(cvGetSize(lpImgBinary), lpImgBinary->depth, lpImgBinary->nChannels);
                if (lpImageOut)
                {
                    SumImageHistogram(lpImgBinary, lpImageOut, vecHistogramSum, 8, 0);

                    int iCount = 0;
                    unsigned long ulSum = 0;
                    std::vector<UINT>::iterator it = vecHistogramSum.begin();
                    while (it != vecHistogramSum.end())
                    {
                        if (*it)
                        {
                            ulSum += *it;
                            iCount++;
                        }

                        it++;
                    }

                    int iAvg = ulSum / iCount;
                    int ithreshold = iAvg / 5;

                    //如果獲取的高度小於50個畫素,認為是空白
                    if (8 > ithreshold)
                        return;

                    RECT rc;
                    ZeroMemory(&rc, sizeof(RECT));
                    rc.right  = lpImageOut->width-1;
                    rc.bottom = lpImageOut->height/2;

                    std::vector<RECT> vecRegionalism;
                    vecRegionalism.clear();

                    statistical_image_chang_count_pos(lpImageOut, rc, vecRegionalism, ithreshold/2, ithreshold/2, false);

                    if (!vecRegionalism.empty())
                    {
                        //if (1 == vecRegionalism.size())
                        //{
                        //    
                        //}
                        //else
                        {
                            std::vector<RECT>::iterator it = vecRegionalism.begin();
                            while (it != vecRegionalism.end())
                            {
                                if (100 > (it->right - it->left))
                                {
                                    it++;

                                    continue;
                                }

                                IplImage* lpImgTemp = Copy_Image_roi_data(lpImgBinary, it->left, 0, it->right, lpImgBinary->height);
                                if (lpImgTemp)
                                {
                                    // 記錄圖片的水平投影
                                    std::vector<RECT> vecRegsm;
                                    vecRegsm.clear();

                                    {    // 圖片畫素統計(水平投影)
                                        IplImage* lpImgOut = cvCreateImage(cvGetSize(lpImgTemp), lpImgTemp->depth, lpImgTemp->nChannels);
                                        if (lpImgOut)
                                        {
                                            vecHistogramSum.clear();

                                            SumImageHistogram(lpImgTemp, lpImgOut, vecHistogramSum, 8, 1);

                                            rc.left = 0;
                                            rc.top  = 0;
                                            rc.right  = lpImgOut->width  - 1;
                                            rc.bottom = lpImgOut->height - 1;
                                            statistical_image_chang_count_pos(lpImgOut, rc, vecRegsm, 8, 8, true);

                                            cvReleaseImage(&lpImgOut);
                                        }
                                    }

                                    std::map<int, RECT> mapRcs;    // 記錄符合條件的橢圓數量
                                    mapRcs.clear();

                                    {    // 圖片輪廓跟蹤
                                        //contour = cvApproxPoly(contour, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1);

                                        IplImage* pContourImg = cvCreateImage(cvGetSize(lpImgTemp), lpImgTemp->depth, 3);
                                        IplImage* pImgeare = cvCreateImage(cvGetSize(lpImgTemp), lpImgTemp->depth, 3);
                                        //cvZero(pContourImg);
                                        //cvZero(pImgeare);

                                        RECT rcClear;
                                        memset(&rcClear, 0, sizeof(RECT));
                                        rcClear.right  = pContourImg->width  - 1;
                                        rcClear.bottom = pContourImg->height - 1;

                                        clear_Image_rect(pContourImg, rcClear);
                                        clear_Image_rect(pImgeare, rcClear);

                                        CvBox2D32f *box;
                                        CvPoint *PointArray;
                                        CvPoint2D32f *PointArray2D32f;

                                        CvScalar external_color;
                                        CvScalar hole_color;
                                        int color;
                                        CvMemStorage* storage = cvCreateMemStorage(0);
                                        CvSeq* contour = 0;

                                        int contours_num = cvFindContours(lpImgTemp, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0, 0)); // 輪廓跟蹤

                                        int iIndex = 0;
                                        //繪製所有輪廓並用橢圓擬合    
                                        while (contour)
                                        {
                                            int i;
                                            int count = contour->total;//輪廓個數    
                                            CvPoint center;
                                            CvSize size;

                                            /*個數必須大於6,這是cvFitEllipse_32f的要求*/
                                            if (count < 6)
                                            {
                                                contour = contour->h_next;
                                                continue;
                                            }

                                            cout << "count: " << count << ", Index: " << iIndex++ << endl;

                                            //分配記憶體給點集    
                                            PointArray = (CvPoint *)malloc(count * sizeof(CvPoint));
                                            PointArray2D32f = (CvPoint2D32f*)malloc(count * sizeof(CvPoint2D32f));

                                            //分配記憶體給橢圓資料    
                                            box = (CvBox2D32f *)malloc(sizeof(CvBox2D32f));

                                            //得到點集(這個方法值得借鑑)    
                                            cvCvtSeqToArray(contour, PointArray, CV_WHOLE_SEQ);

                                            //將CvPoint點集轉化為CvBox2D32f集合    
                                            for (i = 0; i < count; i++)
                                            {
                                                PointArray2D32f[i].x = (float)PointArray[i].x;
                                                PointArray2D32f[i].y = (float)PointArray[i].y;
                                            }

                                            //擬合當前輪廓    
                                            cvFitEllipse(PointArray2D32f, count, box);
                                            double c = sqrt((box->size.height)*(box->size.height) / 4 - (box->size.width)*(box->size.width) / 4);
                                            double lxl = 2 * c / box->size.height;
                                            if ((48.00 < box->size.height && 100.00 < box->size.width) &&
                                                (lpImgBinary->height/3 > box->size.height && lpImgBinary->width/2 > box->size.width))
                                            {
                                                cout << "離心率: " << lxl << ", Height: " << box->size.height << ", Width: " << box->size.width << ", Height/Width: " << (box->size.height/box->size.width) << endl;

                                                //繪製當前輪廓    
                                                cvDrawContours(pImgeare, contour, CV_RGB(0, 0, 0), CV_RGB(0, 0, 0), 0, 3, CV_AA, cvPoint(0, 0));

                                                //將橢圓資料從浮點轉化為整數表示    
                                                center.x = cvRound(box->center.x);
                                                center.y = cvRound(box->center.y);
                                                size.width  = cvRound(box->size.width*0.5);
                                                size.height = cvRound(box->size.height*0.5);
                                                box->angle  = -box->angle;

                                                RECT rc;
                                                rc.left = box->center.x - box->size.height*0.5;
                                                rc.top  = box->center.y - box->size.width *0.5;
                                                rc.right  = rc.left + box->size.height;
                                                rc.bottom = rc.top  + box->size.width;
                                                //mapRcs.insert(std::map<int, RECT>::value_type(rc.top, rc));
                                                //畫橢圓  
                                                if (TRUE == InsertRcMap(mapRcs, rc))  
                                                    cvEllipse(pContourImg, center, size, box->angle, 0, 360, CV_RGB(0, 0, 0), 3, CV_AA, 0);
                                            }

                                            free(PointArray);
                                            free(PointArray2D32f);
                                            free(box);

                                            contour = contour->h_next;
                                        }

                                        cvReleaseMemStorage(&storage);

                                        IplImage* pImgEareBin = get_image_binary(pImgeare, 0);

                                        cvReleaseImage(&pContourImg);
                                        cvReleaseImage(&pImgeare);

                                        if (pImgEareBin)
                                        {
                                            std::vector<RECT> vecRegiona;
                                            vecRegiona.clear();

                                            RECT rcTmp;
                                            memset(&rcTmp, 0, sizeof(RECT));
                                            rcTmp.right  = pImgEareBin->width  - 1;
                                            rcTmp.bottom = pImgEareBin->height - 1;

                                            statistical_image_chang_count_pos(pImgEareBin, rcTmp, vecRegiona, 2, 2, true);

                                            cvReleaseImage(&pImgEareBin);

                                            if (!vecRegiona.empty())
                                            {
                                                int iSize = vecRegiona.size();
                                                int iCount = iSize%2;
                                                if (iCount)
                                                {    // 奇數各

                                                }
                                                else
                                                {    // 偶數個
                                                    std::vector<RECT>::iterator it1, it2;
                                                    it1 = vecRegiona.begin();
                                                    it2 = vecRegiona.begin();
                                                    it2++;

                                                    while (it1 != vecRegiona.end() && it2 != vecRegiona.end())
                                                    {
                                                        RECT rect;
                                                        memset(&rect, 0, sizeof(RECT));
                                                        rect.top    = it1->top;
                                                        rect.bottom = it2->bottom;
                                                        rect.left   = it->left;
                                                        rect.right  = it->right;

                                                        {
                                                            std::vector<RECT>::iterator ittemp1;
                                                            ittemp1 = vecRegsm.begin();

                                                            while (ittemp1 != vecRegsm.end())
                                                            {
                                                                if (64 > abs(rect.top - ittemp1->top))
                                                                {
                                                                    rect.top = __min(rect.top, ittemp1->top);
                                                                }

                                                                if (100 > abs(rect.bottom - ittemp1->bottom))
                                                                {
                                                                    rect.bottom = __max(rect.bottom, ittemp1->bottom);
                                                                }

                                                                ittemp1++;
                                                            }

                                                        }

                                                        mapRC[rect.top] = rect;


                                                        it1++;
                                                        it2++;

                                                        if (it1 != vecRegiona.end() && it2 != vecRegiona.end())
                                                        {
                                                            it1++;
                                                            it2++;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    cvReleaseImage(&lpImgTemp);
                                }

                                it++;
                            }
                        }
                    }
                    else
                    {
                        // 空的白紙
                        return ;
                    }
                    
                    cvReleaseImage(&lpImageOut);
                }
            }

            cvReleaseImage(&lpImgBinary);
        }

        {
            std::map<int, RECT>::iterator it;
            it = mapRC.begin();

            while (it != mapRC.end())
            {
                // 判斷載入圖片的長寬比是否滿足VAT發票的要求
                int itemp = (int)(((double)(it->second.right-it->second.left)/(double)(it->second.bottom-it->second.top))*10.00);    // 一般發票長寬比例為1.5% ~ 1.8%左右
                if (itemp > 20)    
                {    // 清單
                    mapRC.erase(it);

                    if (!mapRC.empty())
                    {
                        it = mapRC.begin();
                        continue;
                    }
                    else
                    {
                        break;
                    }
                }

                it++;
            }
        }

        if (mapRC.empty())
        {
            IplImage* lpImgBinary = get_image_binary(lpImgSrc, 0);
            if (lpImgBinary)
            {
                std::vector<UINT> vecHistogramSum;
                vecHistogramSum.clear();

                removice_noise_image(lpImgBinary, 3);

                cvErode(lpImgBinary, lpImgBinary, NULL, 3);

                // 記錄圖片的水平投影
                std::vector<RECT> vecRegsm;
                vecRegsm.clear();

                {    // 圖片畫素統計(水平投影)
                    IplImage* lpImgOut = cvCreateImage(cvGetSize(lpImgBinary), lpImgBinary->depth, lpImgBinary->nChannels);
                    if (lpImgOut)
                    {
                        vecHistogramSum.clear();

                        SumImageHistogram(lpImgBinary, lpImgOut, vecHistogramSum, 8, 1);

                        RECT rc;
                        rc.left = 0;
                        rc.top  = 0;
                        rc.right  = lpImgOut->width  - 1;
                        rc.bottom = lpImgOut->height - 1;
                        statistical_image_chang_count_pos(lpImgOut, rc, vecRegsm, 8, 8, true);

                        cvReleaseImage(&lpImgOut);
                    }
                }

                if (!vecRegsm.empty())
                {
                    std::vector<RECT>::iterator it;
                    it = vecRegsm.begin();
                    while (it != vecRegsm.end())
                    {
                        if (it->bottom - it->top > 100)
                        {
                            IplImage* lpImgTemp = Copy_Image_roi_data(lpImgBinary, it->left, it->top, it->right, it->bottom);
                            if (lpImgTemp)
                            {
                                IplImage* lpImgOut = cvCreateImage(cvGetSize(lpImgTemp), lpImgTemp->depth, lpImgTemp->nChannels);
                                if (lpImgOut)
                                {
                                    vecHistogramSum.clear();

                                    SumImageHistogram(lpImgTemp, lpImgOut, vecHistogramSum, 8, 0);

                                    RECT rc;
                                    rc.left = 0;
                                    rc.top  = 0;
                                    rc.right  = lpImgOut->width  - 1;
                                    rc.bottom = lpImgOut->height - 1;

                                    std::vector<RECT> vecRegsmons;
                                    vecRegsmons.clear();

                                    statistical_image_chang_count_pos(lpImgOut, rc, vecRegsmons, 3, 3, false);

                                    if (!vecRegsmons.empty())
                                    {
                                        int iCount = 0;
                                        std::vector<RECT>::iterator itor;
                                        itor = vecRegsmons.begin();
                                        while (itor != vecRegsmons.end())
                                        {