一:原理
根据输入参数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是图像分割算法之一。
告示一下:
博客从这个月恢复更新,请大家继续关注,之前消失了一年,完
成了本人的第一本关于图像处理的书初稿写作,谢谢大家厚爱