首先是基本的投影:
/**
* 图像向x轴做投影后的数组
*
* @param imagedata
* @param w
* 宽
* @param h
* 高
* @return
*/
public static int[] xpro(BitSet bitSet, int width, int height) {
int xpro[] = new int[width];
for (int j = 0; j < width; j++) {
for (int i = 0; i < height; i++) {
if (bitSet.get(i * width + j) == true)
xpro[j]++;
}
}
return xpro;
}
public static int[] xpro(ImageData image) {
return xpro(image.getBitSet(), image.getWidth(), image.getHeight());
}
/**
* 图像向y轴做投影后的数组
*
* @param imagedata
* @param w
* @param h
* @return
*/
public static int[] ypro(BitSet bitSet, int width, int height) {
int ypro[] = new int[height];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (bitSet.get(i * width + j) == true)
ypro[i]++;
}
}
return ypro;
}
public static int[] ypro(ImageData image) {
return ypro(image.getBitSet(), image.getWidth(), image.getHeight());
}
然后是基于基本投影的图像分割(割线集合可以用容器,这里不改了):
public static Rectangle[] yproSegment(int[] ypro, int width, int height) {
int[] L = new int[height - 1];// 左割线集合,最多n-1条分割线,且左分割第一项取0,即图片第一行做起点(需要a行位置没有太多空白噪声)
int[] R = new int[height - 1];// 右割线集合
// 两种情况:sku区域起始位置元素为空白区域;起始位置含字符元素
int k1 = 0;
int k2 = 0;
if (ypro[0] != 0) {
k1 = 1;
L[0] = 0;
}
for (int i = 4; i < height; i++) {
if (ypro[i] > 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
&& ypro[i - 3] > 0 && ypro[i - 4] == 0) {
L[k1] = i - 4;
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;
}
}
if (ypro[ypro.length - 1] != 0) {
R[k2] = height;
}
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], width, R[i] - L[i]));
} else {
break;
}
}
Rectangle[] children = new Rectangle[c.size()];
for (int i = 0; i < children.length; i++) {
children[i] = c.get(i);
}
return children;
}
但是有时候图像的sku是粘连的,可以利用图像中sku的特点(每行的宽度相似,比较规范,那么只要不是超过50%粘连,众数就必定是区分的合理阈值,再利用极值点作为分割点,以及众数作为校验参数)
/**
* 纵向自动版面分析(众数参考分析)
*
* @param ypro
* @param im
* @param h
* @param w
* @return
*/
public static int[] ylinelayout(int[] ypro, int width, int height) {
// 投影分割图片
boolean flag = false;
for (int i : ypro) {
if (i == 0) {
flag = true;
break;
}
}
if (flag == false) {
int[] result = { 0, ypro.length };
return result;
}
int[] L = new int[height - 1];// 左割线集合,最多n-1条分割线,且左分割第一项取0,即图片第一行做起点(需要a行位置没有太多空白噪声)
int[] R = new int[height - 1];// 右割线集合
// 两种情况:sku区域起始位置元素为空白区域;起始位置含字符元素
int k1 = 0;
int k2 = 0;
if (ypro[0] != 0) {
k1 = 1;
L[0] = 0;
}
for (int i = 4; i < height; i++) {
if (ypro[i] > 0 && ypro[i - 1] > 0 && ypro[i - 2] > 0
&& ypro[i - 3] > 0 && ypro[i - 4] == 0) {//左边界特征
L[k1] = i - 4;
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;
}
}
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);
}
// 得到初次分割的所有“字符”的高度
if (gap.length == 1) {
int[] result = { L[0], R[0] };
return result;
}
if (gap.length == 2) {
int[] result = { L[0], R[0], L[1], R[1] };
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进行二次分割(粘连字符二次分割函数)
log.info("发现异常点:" + gap[i]);
int[] newline = improveSegment(c.get(i * 2), c.get(i * 2 + 1),
ypro, gap[i], Te);
if (newline != null) {
for (int j : newline) {
newc.add(j);
log.info("newline:" + j);
}
}
}
}
int begin = 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 = begin; 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 + 2 + time * 2, th + 1);
begin = 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;
}
极值点查找
/**
* 二次分割(找到粘连中的k个线,k》=2)
*
* @param localypro
* @param begin
* @param t1
* @return
*/
public static int findline(int[] localypro, int begin, int t1) {
int findline = 0;
int len = localypro.length - t1;
for (int i = begin; i < len; i++) {
int kL = 0;
int kR = 0;
for (int j = 1; j < t1; j++) {
if (localypro[i] <= localypro[i - j]) {
kL++;
} else {
break;
}
if (localypro[i] <= localypro[i + j]) {
kR++;
} else {
break;
}
}
if (kL == t1 - 1 && kR == t1 - 1) {
findline = i;
break;
}
}
return findline;
}
/**
* 二次分割
*
* @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) {
if (Te <= 8)
return null;
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 = i + t2) {
int findline = findline(localypro, i, t1);
c.add(a + findline);
}
return ArrayUtils.toPrimitive(c.toArray(new Integer[0]));
}