3.4.2 平均值法

编程实现

[1] 在CBarRecog类中添加相应的函数PreProcess(),对条形码图像进行预处理,得出宽度序列,具体实现代码如代码3-2所示。

代码3-2 PreProcess()函数

        BOOL CBarRecog::PreProcess()
        {
            //阈值设为128
            BinaryImage(160);
            int i, j;
            int tempMax;
            int tempArray[1000];
            //进行水平方向和垂直方向上的直方图统计
            for(i=0; i<ImageHeight; i++)
                arPixelV[i] = 0;
            for(i=0; i<ImageWidth; i++)
                arPixelH[i] = 0;
            for(i=0; i<ImageHeight; i++)
            {
                for(j=0; j<ImageWidth; j++)
                {
                    if(ImageArray[i][j] == 1)
                    {
                          arPixelV[i] += 1;
                          arPixelH[j] += 1;
                    }
                }
            }
            //寻找包含条形码的区域
            //先寻找水平方向上黑像素最大的行
            tempMax = 0;
            for(i=0; i<ImageHeight; i++)
            {
                if(arPixelV[i]>tempMax)
                    tempMax = arPixelV[i];
                arMark[i] = false;
            }
            for(i=0; i<ImageHeight-1; i++)
            {
                //计算差分
                arDifference[i] = arPixelV[i+1] - arPixelV[i];
                //如果该行像素足够多且变化不大,标记为true
                if( (abs(arDifference[i])<20) && (arPixelV[i]>(0.75*tempMax)) )
                    arMark[i] = true;
            }
            //确定包含条形码的行
            int iLengthThrehold = 40;
            int iCount;
            for(i=0; i<ImageHeight-iLengthThrehold; i++)
            {
                iCount = 0;
                for(j=0; j<iLengthThrehold; j++)
                {
                    if(arMark[i+j] == true)
                        iCount++;
                }
                if(iCount >= 37)
                {
                    ImageTop = i+10;     //确定顶部
                    break;
                }
            }
            for(i=ImageHeight-1; i>=iLengthThrehold-1; i--)
            {
                iCount = 0;
                for(j=0; j<iLengthThrehold; j++)
                {
                    if(arMark[i-j] == true)
                        iCount++;
                }
                if(iCount >= 37)//iLengthThrehold-3
                {
                    ImageBottom = i-10;      //确定底部
                    break;
                }
            }
            //寻找左边缘,为了保证稳健性,在已经确定的上下边界内全局搜索
            for(i=ImageTop; i<=ImageBottom; i++)
            {
                for(j=20; j<ImageWidth; j++)
                {
                    if( (ImageArray[i][j-1]==0) && (ImageArray[i][j]==1) )
                    {
                        arLeftEdge[i] = j;
                        break;
                    }
                }
            }
{
            //为消除噪声的干扰,下面确定准确的左边界
            tempMax = 0;
            int iMax = 0;
            for(i=ImageTop; i<=ImageBottom; i++)
            {
                if(arLeftEdge[i] > tempMax)
                    tempMax = arLeftEdge[i];
                    iMax = i;
                }
            }
            //倾斜度不能大于1/10
            iCount = 0;
            for(i=ImageTop; i<=ImageBottom; i++)
            {
                if( abs(tempMax-arLeftEdge[i]) < abs(i-iMax)/6+1 )
                {
                }
                    iCount++;
            }
            if( (iCount/(ImageBottom-ImageTop))<0.6 )
                return false;
            //调整起点
            for(i=iMax; i>ImageTop; i--)
            {
                if( abs(arLeftEdge[i]-arLeftEdge[i-1])>=2 )
                {
                    if(ImageArray[i-1][arLeftEdge[i]]-ImageArray[i-1][arLeftEdge[i]-1]== 1)
                        arLeftEdge[i-1] = arLeftEdge[i];
                    else if(ImageArray[i-1][arLeftEdge[i]-1]-ImageArray[i-1][arLeft Edge[i]-2] == 1)
                        arLeftEdge[i-1] = arLeftEdge[i]-1;
                    else if(ImageArray[i-1][arLeftEdge[i]+1]-ImageArray[i-1][arLeft Edge[i]] == 1)
                        arLeftEdge[i-1] = arLeftEdge[i]+1;
                    else
                        arLeftEdge[i-1] = arLeftEdge[i];
                }
            }
            for(i=iMax; i<ImageBottom; i++)
            {
                if(i == ImageBottom)
                    break;
                if( abs(arLeftEdge[i]-arLeftEdge[i+1])>=2 )
                {
                    if(ImageArray[i+1][arLeftEdge[i]]-ImageArray[i+1][arLeftEdge[i]-1] == 1)
                        arLeftEdge[i+1] = arLeftEdge[i];
                    else if(ImageArray[i+1][arLeftEdge[i]-1]-ImageArray[i+1][arLeft Edge[i]-2] == 1)
                        arLeftEdge[i+1] = arLeftEdge[i]-1;
                    else if(ImageArray[i+1][arLeftEdge[i]+1]-ImageArray[i+1][arLeft Edge[i]] == 1)
                        arLeftEdge[i+1] = arLeftEdge[i]+1;
                    else
                        arLeftEdge[i+1] = arLeftEdge[i];
                }
            }
            int n;
            //搜索出所有的宽度
            for(n=0; n<29; n++)
            {
                //搜索条的右边缘
                for(i=ImageTop; i<=ImageBottom; i++)
                {
                    for(j = arLeftEdge[i]+1; j<ImageWidth; j++)
                    {
                        if( (ImageArray[i][j-1]==1) && (ImageArray[i][j]==0) )
                        {
                            arLeftEdge1[i] = j;
                            break;
                        }
                    }
                    arDelta[i] = arLeftEdge1[i] - arLeftEdge[i];
                }
                //假定条和空的宽度最多为11
                //排序,可以认为最中间的5个宽度是平均宽度
                for(i=ImageTop; i<ImageBottom; i++)
                    tempArray[i] = arDelta[i];
                for(i=ImageTop; i<ImageBottom; i++)
                {
                    for(j=ImageBottom; j>i; j--)
                    {
                        int tempSwap;
                        if(tempArray[j] < tempArray[j-1])
                        {
                            tempSwap = tempArray[j];
                            tempArray[j] = tempArray[j-1];
                            tempArray[j-1] = tempSwap;
                        }
                    }
                }
                if(tempArray[ImageTop+(ImageBottom-ImageTop)/2+2]-tempArray[ImageTop+(ImageBottom-ImageTop)/2-2]>1)
                    return false;
                else
                    arWidth[2*n] = tempArray[ImageTop+(ImageBottom-ImageTop)/2];
                //调整下一列边缘
                for(i=ImageTop; i<=ImageBottom; i++)
                {
                    if(abs(arDelta[i] - arWidth[2*n])>2)
                        arLeftEdge1[i] = arLeftEdge[i] + arWidth[2*n];
                    arLeftEdge[i] = arLeftEdge1[i];
                }
                //搜索空的右边缘
                for(i=ImageTop; i<=ImageBottom; i++)
                {
                    for(j = arLeftEdge[i]+1; j<ImageWidth; j++)
                    {
                        if( (ImageArray[i][j-1]==0) && (ImageArray[i][j]==1) )
                        {
                            arLeftEdge1[i] = j;
                            break;
                        }
                    }
                    arDelta[i] = arLeftEdge1[i] - arLeftEdge[i];
                }
                //假定条和空的宽度最多为11
                //排序,可以认为最中间的5个宽度是平均宽度
                for(i=ImageTop; i<ImageBottom; i++)
                    tempArray[i] = arDelta[i];
                for(i=ImageTop; i<ImageBottom; i++)
                {
                    for(j=ImageBottom; j>i; j--)
                    {
                        int tempSwap;
                        if(tempArray[j] < tempArray[j-1])
                        {
                            tempSwap = tempArray[j];
                            tempArray[j] = tempArray[j-1];
                            tempArray[j-1] = tempSwap;
                        }
                    }
                }
                if(tempArray[ImageTop+(ImageBottom-ImageTop)/2+2]-tempArray[ImageTop+
                    (ImageBottom-ImageTop)/2-2]>1)
                    return false;
                else
                    arWidth[2*n+1] = tempArray[ImageTop+(ImageBottom-ImageTop)/2];
                //调整下一列边缘
                for(i=ImageTop; i<=ImageBottom; i++)
                {
                    if(abs(arDelta[i] - arWidth[2*n+1])>2)
                        arLeftEdge1[i] = arLeftEdge[i] + arWidth[2*n+1];
                    arLeftEdge[i] = arLeftEdge1[i];
                }
            }
            //搜索最后一个条的右边缘
            for(i=ImageTop; i<=ImageBottom; i++)
            {
                for(j = arLeftEdge[i]+1; j<ImageWidth; j++)
                {
                    if( (ImageArray[i][j-1]==1) && (ImageArray[i][j]==0) )
                    {
                        arLeftEdge1[i] = j;
                        break;
                    }
                }
                arDelta[i] = arLeftEdge1[i] - arLeftEdge[i];
            }
            //假定条和空的宽度最多为11
            //排序,可以认为最中间的5个宽度是平均宽度
            for(i=ImageTop; i<ImageBottom; i++)
                tempArray[i] = arDelta[i];
            for(i=ImageTop; i<ImageBottom; i++)
            {
                for(j=ImageBottom; j>i; j--)
                {
                    int tempSwap;
                    if(tempArray[j] < tempArray[j-1])
                    {
                        tempSwap = tempArray[j];
                        tempArray[j] = tempArray[j-1];
                        tempArray[j-1] = tempSwap;
                    }
                }
            }
            if(tempArray[ImageTop+(ImageBottom-ImageTop)/2+2]-tempArray[ImageTop+(Imag
                eBottom-ImageTop)/2-2]>1)
                return false;
            else
                arWidth[2*n] = tempArray[ImageTop+(ImageBottom-ImageTop)/2];
            //调整下一列边缘
            for(i=ImageTop; i<=ImageBottom; i++)
            {
                if(abs(arDelta[i] - arWidth[2*n+1])>2)
                    arLeftEdge1[i] = arLeftEdge[i] + tempArray[ImageTop+(ImageBottom-
                        ImageTop)/2];
                arLeftEdge[i] = arLeftEdge1[i];
            }
            return true;
        }

[2] 在CBarRecog类中添加相应的函数:Recognize(),对条形码图像进行字符识别,具体实现代码如代码3-3所示。

代码3-3 Recognize()函数

        BOOL CBarRecog::Recognize()
        {
            //总共有7×12+3×2+5= 95个单位宽度
            //4×12+3×2+5=59个宽度
            int i;
            int result[12];
            double mx = 0.0; //平均宽度
            for(i=0; i<59; i++)
                mx += (double)arWidth[i];
            mx /= 95.0;
            //起始条纹
            for(i=0; i<3; i++)
            {
                double dTemp = (double)arWidth[i]/mx;
                if( dTemp<0.6 || dTemp>1.4 )
                    break;
            }
            //起始码不符合要求
            //if(i<3)
            //  return false;
            //识别前6
            for(i=0; i<6; i++)
            {
                result[i]  =  JudgNum(arWidth[i*4+3],  arWidth[i*4+4],  arWidth[i*4+5],arWidth[i*4+6], mx);
            }
            //识别后6
            for(i=6; i<12; i++)
            {
                result[i]  =  JudgNum(arWidth[i*4+8],  arWidth[i*4+9],  arWidth[i*4+10],arWidth[i*4+11], mx);
            }
            //判断码制
            if( result[0]==7 && result[1]==7 )
            {
                strCodeStyle = "ISSN";
            }
            else if( result[0]==7 && result[1]==8 )
            {
                strCodeStyle = "ISBN";
            }
            else
                strCodeStyle = "Unknown!";
            //判断是否全部识别出来
            for(i=0; i<12; i++)
                if(result[i] == -1)
                    return false;
            CString strTemp;
            strCodeNumber.Format("");
            for(i=0; i<12; i++)
            {
                strTemp.Format("%d", result[i]);
                strCodeNumber += strTemp;
            }
            return true;
        }

[3] 代码3-3中调用了函数JudgNumt(),该函数的实现代码如代码3-4所示。

代码3-4 JudgNumt()函数

        int CBarRecog::JudgNum(int w1, int w2, int w3, int w4, double mx)
        {
            double a1, a2, a3;
            int ia1, ia2, ia3;
            a1 = (double)(w1+w2)/mx;
            a2 = (double)(w2+w3)/mx;
            a3 = (double)(w3+w4)/mx;
            ia1 = (int)(a1+0.5);
            ia2 = (int)(a2+0.5);
            ia3 = (int)(a3+0.5);
            //判断该码值
            if( (ia1==5 && ia2==3 && ia3==2) || (ia1==2 && ia2==3 && ia3==5) )
                return 0;
            if( (ia1==4 && ia2==4 && ia3==3) || (ia1==3 && ia2==4 && ia3==4) )
            {
                if(ia1 == 4)
                {
                    double dw2 = (double)w2/mx;
                    if(dw2 < 2.4)
                          return 1;
                    else if(dw2 > 2.6)
                        return 7;
                    else return -1;
                }
                if(ia1 == 3)
                {
                    double dw3 = (double)w3/mx;
                    if(dw3 < 2.4)
                        return 1;
                    else if(dw3 > 2.6)
                        return 7;
                    else return -1;
                }
            }
            if( (ia1==3 && ia2==3 && ia3==4) || (ia1==4 && ia2==3 && ia3==3) )
            {
                if(ia1 == 3)
                {
                    double dw4 = (double)w4/mx;
                    if(dw4 < 2.4)
                        return 2;
                    else if(dw4 > 2.6)
                        return 8;
                    else return -1;
                }
                if(ia1 == 4)
                {
                    double dw1 = (double)w1/mx;
                    if(dw1 < 2.4)
                        return 2;
                    else if(dw1 > 2.6)
                        return 8;
                    else return -1;
                }
            }
            if( (ia1==5 && ia2==5 && ia3==2) || (ia1==2 && ia2==5 && ia3==5) )
                return 3;
            if( (ia1==2 && ia2==4 && ia3==5) || (ia1==5 && ia2==4 && ia3==2) )
                return 4;
            if( (ia1==3 && ia2==5 && ia3==4) || (ia1==4 && ia2==5 && ia3==3) )
                return 5;
            if( (ia1==2 && ia2==2 && ia3==5) || (ia1==5 && ia2==2 && ia3==2) )
                return 6;
            if( (ia1==4 && ia2==2 && ia3==3) || (ia1==3 && ia2==2 && ia3==4) )
                return 9;
            return false;
        }