VC++ 利用Opencv 做的一个发票识别程序,识别有误

时间:2024-04-14 08:26:20

如题,如图
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())
                                        {
                                            if (itor->right - itor->left > 100)
                                            {
                                                RECT rt;
                                                rt.left   = itor->left;
                                                rt.top    = it->top;
                                                rt.right  = itor->right;
                                                rt.bottom = it->bottom;

                                                mapRC[rt.top+iCount] = rt;
                                            }

                                            iCount++;

                                            itor++;
                                        }
                                    }

                                    cvReleaseImage(&lpImgOut);
                                }

                                cvReleaseImage(&lpImgTemp);
                            }
                        }

                        it++;
                    }
                }

                cvReleaseImage(&lpImgBinary);
            }
        }

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

            while (it != mapRC.end())
            {
                cv::Point pt1, pt2;
                pt1.x = it->second.left;
                pt1.y = it->second.top;

                pt2.x = it->second.right;
                pt2.y = it->second.bottom;

                //cv::rectangle(cv::cvarrToMat(&lpImgSrc), pt1, pt2, CV_RGB(0, 255, 0), 4, 8, 0);

                CvPoint pt[4];
                pt[0].x = it->second.left;
                pt[0].y = it->second.top;

                pt[1].x = it->second.right;
                pt[1].y = it->second.top;

                pt[2].x = it->second.right;
                pt[2].y = it->second.bottom;

                pt[3].x = it->second.left;
                pt[3].y = it->second.bottom;

        
                cvLine( lpImgSrc, pt[0], pt[1], CV_RGB(0, 255, 0), 8, 8, 0);
                cvLine( lpImgSrc, pt[1], pt[2], CV_RGB(0, 255, 0), 8, 8, 0);
                cvLine( lpImgSrc, pt[2], pt[3], CV_RGB(0, 255, 0), 8, 8, 0);
                cvLine( lpImgSrc, pt[3], pt[0], CV_RGB(0, 255, 0), 8, 8, 0);

                it++;
            }

            char szFile[MAX_PATH];
            memset(szFile, 0, sizeof(char)*MAX_PATH);
            strcpy(szFile, pTifFile);

            char* pPos = strrchr(szFile, '.');
            if (pPos)
            {
                strcpy(pPos, "_result");

                const char * pP = strrchr(pTifFile, '.');
                if (pP)
                {
                    strcat(szFile, pP);
                }
            }
            
            pPos = strrchr(szFile, '.');
            if (pPos && 0 == stricmp(pPos, ".jpg"))
            {
                cvSaveImage(szFile, lpImgSrc, p);
            }
            else
            {
                cvSaveImage(szFile, lpImgSrc);
            }
        }
        
        cvReleaseImage(&lpImgSrc);
    }
}