基于投影的字符版面分析java代码

首先介绍算法思路:图像对应方向的投影,就是在该方向取一条直线,统计垂直于该直线(轴)的图像上的像素的黑点数量,累加求和作为该轴该位置的值;基于图像投影的切割就是将图像映射成这种特征后,基于这种特征判定图像的切割位置(坐标),用这个坐标来切割原图像,得到目标图像。

java代码实现:
java的图像处理,这里大部分是由im.read读取,bufferedimage,然后转为二值bitset做处理(0,1或者说是true和false,一个bitset是整张图片的0,1像素值的一维行向量。bitset.length=width*height)

/**
     * 图像向x轴做投影后的数组
     * 
     * @param imagedata
     * @param w
     * @param h
     * @return
     */
    public static int[] xpro(BitSet bitSet, int w, int h) {
        int xpro[] = new int[w];
        for (int j = 0; j < w; j++) {
            for (int i = 0; i < h; i++) {
                if (bitSet.get(i * w + j) == true)
                    xpro[j]++;
            }
        }
        return xpro;
    }

    /**
     * 图像向y轴做投影后的数组
     * 
     * @param imagedata
     * @param w
     * @param h
     * @return
     */
    public static int[] ypro(BitSet bitSet, int w, int h) {
        int ypro[] = new int[h];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                if (bitSet.get(i * w + j) == true)
                    ypro[i]++;
            }
        }
        return ypro;
    }

简单的基于投影的图像分割:

public static Rectangle[] yproSegment(int[] ypro, int w, int h) {

        int[] L = new int[h - 1];// 左割线集合,最多n-1条分割线,且左分割第一项取0,即图片第一行做起点(需要a行位置没有太多空白噪声)
        int[] R = new int[h - 1];// 右割线集合
        // 两种情况:sku区域起始位置元素为空白区域;起始位置含字符元素
        int k1 = 0;
        int k2 = 0;
        if (ypro[0] == 0) {
            k1 = 0;
            k2 = 0;
            for (int i = 4; i < h; i++) {
                if (ypro[i] > 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] == 0) {
                    L[k1] = i - 3;
                    k1++;
                } else if (ypro[i] == 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] > 0) {
                    R[k2] = i;
                    k2 += 1;
                }
            }
        } else {
            k1 = 1;
            k2 = 0;
            for (int i = 4; i < h; i++) {
                if (ypro[i] > 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] == 0) {
                    L[k1] = i - 3;
                    k1++;
                } else if (ypro[i] == 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] > 0) {
                    R[k2] = i;
                    k2 += 1;
                }
            }
            L[0] = 0;
        }
        if (ypro[ypro.length - 1] != 0) {
            R[k2] = h;
        }
        List c = new ArrayList();
        for (int i = 0; i < R.length; i++) {
            if (R[i] != 0 && L[i] < R[i]) {
                c.add(new Rectangle(0, L[i], w, R[i] - L[i]));
            } else {
                break;
            }
        }
        // System.out.println("yrpo");
        // for(int i:ypro){
        // System.out.println(i);
        // }
        // System.out.println("Llast:"+L[c.size()-1]);
        // System.out.println("Rast:"+R[c.size()-1]);
        Rectangle[] children = new Rectangle[c.size()];
        for (int i = 0; i < children.length; i++) {
            children[i] = c.get(i);
        }
        return children;
    }

利用粘连区域特点(sku每行高度是相似的,也就是说高度的众数可以作为衡量是否粘连的阈值,然后以极值点进行sku的粘连自动分割:

/**
     * 
     * @param ypro
     * @return
     */
    public static Rectangle[] yproAutoSegment(int[] ypro, int w, int h) {
        int[] allline = ylinelayout(ypro, w, h);
        if (allline.length == 0)
            return null;
        Rectangle[] yproSegment = new Rectangle[allline.length / 2];
        for (int i = 0; i < yproSegment.length; i++) {
            // yproSegment[i]= new Imxxyy(0, w, allline[i*2], allline[i*2+1]);
            yproSegment[i] = new Rectangle(0, allline[i * 2], w,
                    allline[i * 2 + 1] - allline[i * 2]);
        }
        return yproSegment;
    }

/**
     * 纵向自动版面分析(众数参考分析)
     * 
     * @param ypro
     * @param im
     * @param h
     * @param w
     * @return
     */
    public static int[] ylinelayout(int[] ypro, int w, int h) {
        // 投影分割图片
        boolean flag = false;
        for (int i : ypro) {
            if (i == 0) {
                flag = true;
                break;
            }
        }
        // System.out.println("ypro:");
        // for(int i:ypro){
        // System.out.println(i);
        // }
        // System.out.println("flag:"+flag);
        if (flag == false) {
            int[] result = { 0, ypro.length };
            return result;
        }
        int[] L = new int[h - 1];// 左割线集合,最多n-1条分割线,且左分割第一项取0,即图片第一行做起点(需要a行位置没有太多空白噪声)
        int[] R = new int[h - 1];// 右割线集合
        // 两种情况:sku区域起始位置元素为空白区域;起始位置含字符元素
        int k1 = 0;
        int k2 = 0;
        if (ypro[0] == 0) {
            k1 = 0;
            k2 = 0;
            for (int i = 4; i < h; i++) {
                if (ypro[i] > 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] == 0) {
                    L[k1] = i - 3;
                    k1++;
                } else if (ypro[i] == 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] > 0) {
                    R[k2] = i;
                    k2 += 1;
                }
            }
        } else {
            k1 = 1;
            k2 = 0;
            for (int i = 4; i < h; i++) {
                if (ypro[i] > 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] == 0) {
                    L[k1] = i - 3;
                    k1++;
                } else if (ypro[i] == 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
                        && ypro[i - 3] > 0 && ypro[i - 4] > 0) {
                    R[k2] = i;
                    k2 += 1;
                }
            }
            L[0] = 0;
        }
        if (ypro[ypro.length - 1] != 0) {
            R[k2 + 1] = ypro.length - 1;
        }

        ArrayList c = new ArrayList();
        for (int i = 0; i < R.length; i++) {
            if (R[i] != 0 && L[i] < R[i]) {
                c.add(L[i]);
                c.add(R[i]);
            } else {
                break;
            }
        }

        int[] gap = new int[c.size() / 2];// 间隙
        for (int i = 0; i < gap.length; i++) {
            gap[i] = c.get(i * 2 + 1) - c.get(i * 2);
        }
        // log.info("L.length"+L.length);
        // log.info("L.length"+R.length);
        // log.info("c.size:"+c.size());
        // log.info("gap.length:"+gap.length );
        // 得到初次分割的所有“字符”的高度
        if (gap.length == 1) {
            int[] result = { L[0], R[0] };
            return result;
        }
        int Te = (int) (catchE(gap) + 0.5);
        ArrayList newc = new ArrayList();
        for (int i = 0; i < gap.length; i++) {
            if (gap[i] >= (int) (Te * 1.5 + 0.5)) {
                // 对异常gap进行二次分割(粘连字符二次分割函数)
                int[] newline = improveSegment(c.get(i * 2), c.get(i * 2 + 1),
                        ypro, gap[i], Te);
                for (int j : newline) {
                    newc.add(j);
                }
            }
        }
        int bbegin = 0;
        ArrayList allline = new ArrayList();
        allline.addAll(c);
        int time = 0;
        for (int i = 0; i < newc.size(); i++) {
            int th = newc.get(i);
            for (int j = bbegin; j < c.size() - 1; j++) {
                if (c.get(j) < th && c.get(j + 1) > th) {
                    allline.add(j + 1 + time * 2, th);
                    allline.add(j + 1 + time * 2, th);
                    bbegin = j;
                    time++;
                    break;
                }
            }
        }
        return ArrayUtils.toPrimitive(allline.toArray(new Integer[0]));
    }

/**
     * 找出众数范围(无补偿众数)
     * 
     * @param gap
     * @return
     */
    public static float catchE(int[] gap) {

        float Te = 0;
        int[] g = gap.clone();
        Arrays.sort(g);
        int[] times = new int[g.length];
        for (int i = 0; i < gap.length; i++) {
            for (int j = 0; j < g.length; j++) {
                if (g[j] == gap[i]) {
                    times[j]++;
                }
            }
        }
        // 得到各gap的出现次数
        int tt = 0;
        int num = 0;
        for (int i = 0; i < times.length; i++) {
            if (times[i] > tt) {
                tt = times[i];
                num = i;
            }
        }
        Te = g[num];
        return Te;
    }
极值点的二次分割
/**
     * 二次分割
     * 
     * @param a
     * @param b
     * @param ypro
     * @param gapE
     * @param Te
     * @return
     */
    public static int[] improveSegment(int a, int b, int[] ypro, float gapE,
            int Te) {
        int[] localypro = Arrays.copyOfRange(ypro, a, b + 1);
        // 以t2作为跃迁步长,避免同一区域出现多条分割线,以t1作为分割线阈值,以找到精确分割线
        ArrayList c = new ArrayList();
        int t1 = Te - 8;
        int t2 = Te - 5;
        for (int i = t2; i < localypro.length - t2; i++) {
            int findline = findline(localypro, t2, t1);
            c.add(a + findline);
            i = i + t2;
            if (i > localypro.length) {
                break;
            }

        }
        return ArrayUtils.toPrimitive(c.toArray(new Integer[0]));
    }

公司电脑只装了eclipse,以后补充:
matlab代码实现:
和java代码类似,只是matlab和python是将图像存为二维数组,所以简单的(i,j)遍历就行了,而java是存在一维数组里,需要(i*width,j)
python代码实现:

你可能感兴趣的:(图像处理,java学习)