一:原理
根据输入参数blockSize的大小,将图像分块,决定每块的中心通过该像素块内所有
像素之和的均值与该块内部每个像素比较,RGB值之间几何距离最小为新的中心,迭
代更新运算,直到达到输入参数声明的最大循环次数为止,然后输出结果图像即可。
二:程序实现
类MyCluster,存储每个像素块中心的信息,计算中心位置。
类SuperPixelsFilter, 滤镜实现,完成六边形网格分割的主要功能,其中距离计算,基
于欧几里德距离公式。
三:效果
原图:
效果:
四:完全源代码
package com.gloomyfish.image.cluster.effect; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.gloomyfish.filter.study.AbstractBufferedImageOp; public class SuperPixelsFilter extends AbstractBufferedImageOp { private double[] distances; private int[] labels; private MyCluster[] clusters; private int maxClusteringLoops = 50; private double blockSize; private double modifier; public SuperPixelsFilter() { blockSize = 16; modifier = 130; } public double getBlockSize() { return blockSize; } public void setBlockSize(double blockSize) { this.blockSize = blockSize; } public double getModifier() { return modifier; } public void setModifier(double modifier) { this.modifier = modifier; } @Override public BufferedImage filter(BufferedImage src, BufferedImage dest) { int width = src.getWidth(); int height = src.getHeight(); if ( dest == null ) dest = createCompatibleDestImage( src, null ); int[] inPixels = new int[width*height]; getRGB( src, 0, 0, width, height, inPixels ); int index = 0; // initialization distances = new double[width*height]; labels = new int[width*height]; Arrays.fill(distances, Integer.MAX_VALUE); Arrays.fill(labels, -1); initClusters(width, height, inPixels, blockSize, modifier); // loop to get all block/cells, image segmentation int loops = 0; boolean pixelChangedCluster = true; while (pixelChangedCluster&&loops<maxClusteringLoops) { pixelChangedCluster = false; loops++; // for each cluster center C for (int i=0;i<clusters.length;i++) { MyCluster c = clusters[i]; // for each pixel i in 2S region around // cluster center int xs = Math.max((int)(c.avg_x-blockSize),0); int ys = Math.max((int)(c.avg_y-blockSize),0); int xe = Math.min((int)(c.avg_x+blockSize),width); int ye = Math.min((int)(c.avg_y+blockSize),height); for (int y=ys;y<ye;y++) { for (int x=xs;x<xe;x++) { int pos = x+width*y; int tr = (inPixels[pos] >> 16) & 0xff; int tg = (inPixels[pos] >> 8) & 0xff; int tb = inPixels[pos] & 0xff; double D = c.distance(x, y, tr, tg, tb, blockSize, modifier, width, height); if ((D<distances[pos])&&(labels[pos]!=c.id)) { distances[pos] = D; labels[pos] = c.id; pixelChangedCluster = true; } } // end for x } // end for y } // end for clusters // reset clusters for (index=0;index<clusters.length;index++) { clusters[index].reset(); } // add every pixel to cluster based on label for (int y=0;y<height;y++) { for (int x=0;x<width;x++) { int pos = x+y*width; int tr = (inPixels[pos] >> 16) & 0xff; int tg = (inPixels[pos] >> 8) & 0xff; int tb = inPixels[pos] & 0xff; clusters[labels[pos]].addPixel(x, y, tr, tg, tb); } } // calculate centers for (index=0;index<clusters.length;index++) { clusters[index].calculateCenter(); } } // Create output image with pixel edges for (int y=1;y<height-1;y++) { for (int x=1;x<width-1;x++) { int id1 = labels[x+y*width]; int id2 = labels[(x+1)+y*width]; int id3 = labels[x+(y+1)*width]; if (id1!=id2||id1!=id3) { int pos = x+y*width; inPixels[pos] = (255 << 24) | (0 << 16) | (0 << 8) | 0; } } } setRGB( dest, 0, 0, width, height, inPixels ); return dest; } public void initClusters(int width, int height, int[] input, double S, double m) { List<MyCluster> temp = new ArrayList<MyCluster>(); boolean even = false; double xstart = 0; int id = 0; for (double y = S / 2; y < height; y += S) { // 创建六边形网格 if (even) { xstart = S / 2.0; even = false; } else { xstart = S; even = true; } for (double x = xstart; x < width; x += S) { int index = (int) (x + y * width); int tr = (input[index] >> 16) & 0xff; int tg = (input[index] >> 8) & 0xff; int tb = input[index] & 0xff; MyCluster c = new MyCluster(id, tr, tg, tb, (int) x, (int) y, S, m); temp.add(c); id++; } } clusters = new MyCluster[temp.size()]; for (int i = 0; i < temp.size(); i++) { clusters[i] = temp.get(i); } } }
MyCluster类代码:
package com.gloomyfish.image.cluster.effect; public class MyCluster { int id; double inv = 0; // inv variable for optimization double pixelCount; // pixels in this cluster double avg_red; // average red value double avg_green; // average green value double avg_blue; // average blue value double sum_red; // sum red values double sum_green; // sum green values double sum_blue; // sum blue values double sum_x; // sum x double sum_y; // sum y double avg_x; // average x double avg_y; // average y public MyCluster(int id, int in_red, int in_green, int in_blue, int x, int y, double S, double m) { // inverse for distance calculation this.inv = 1.0 / ((S / m) * (S / m)); this.id = id; addPixel(x, y, in_red, in_green, in_blue); // calculate center with initial one pixel calculateCenter(); } public void reset() { avg_red = 0; avg_green = 0; avg_blue = 0; sum_red = 0; sum_green = 0; sum_blue = 0; pixelCount = 0; avg_x = 0; avg_y = 0; sum_x = 0; sum_y = 0; } /* * Add pixel color values to sum of previously added color values. */ void addPixel(int x, int y, int in_red, int in_green, int in_blue) { sum_x += x; sum_y += y; sum_red += in_red; sum_green += in_green; sum_blue += in_blue; pixelCount++; } public void calculateCenter() { // Optimization: using "inverse" // to change divide to multiply double inv = 1 / pixelCount; avg_red = sum_red * inv; avg_green = sum_green * inv; avg_blue = sum_blue * inv; avg_x = sum_x * inv; avg_y = sum_y * inv; } double distance(int x, int y, int red, int green, int blue, double S, double m, int w, int h) { // power of color difference between // given pixel and cluster center double dx_color = (avg_red - red) * (avg_red - red) + (avg_green - green) * (avg_green - green) + (avg_blue - blue) * (avg_blue - blue); // power of spatial difference between // given pixel and cluster center double dx_spatial = (avg_x - x) * (avg_x - x) + (avg_y - y) * (avg_y - y); // Calculate approximate distance D // double D = dx_color+dx_spatial*inv; // Calculate squares to get more accurate results double D = Math.sqrt(dx_color) + Math.sqrt(dx_spatial * inv); return D; } }五:参考这里
该滤镜是SuperPixel算法的简单应用,多数时候,我们可能更熟悉
K-Means等图像分割算法,其实SuperPixel是图像分割算法之一。
告示一下:
博客从这个月恢复更新,请大家继续关注,之前消失了一年,完
成了本人的第一本关于图像处理的书初稿写作,谢谢大家厚爱