zoukankan      html  css  js  c++  java
  • 基于投影的字符版面分析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<Rectangle> c = new ArrayList<Rectangle>();
            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<Integer> c = new ArrayList<Integer>();
            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<Integer> newc = new ArrayList<Integer>();
            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<Integer> allline = new ArrayList<Integer>();
            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<Integer> c = new ArrayList<Integer>();
            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代码实现:

  • 相关阅读:
    神仙题1.0
    一些小技巧(持续更新。。)
    模板(持续更新中。。)
    「CTS2019 | CTSC2019」氪金手游(容斥+概率计数)
    Emacs配置
    AGC034E Complete Compres(dp)
    「清华集训 2017」榕树之心(换根dp)
    [NOI2018]冒泡排序 (卡特兰数)
    「清华集训 2017」小 Y 和二叉树 (贪心)
    「雅礼集训 2018 Day1」图 (dp套dp)
  • 原文地址:https://www.cnblogs.com/zhangdebin/p/5567922.html
Copyright © 2011-2022 走看看