Processingでボロノイフィルタを作ったので残しておくことにしました。
[ 使用例 ]
[ プログラム ]
プログラムを実行するとpdeファイルと同じディレクトリ内にフィルタリングされた画像が保存されます。
//母点の間隔 int th = 10; //平均化オンオフ boolean b = true; void setup() { PImage i = loadImage("入力画像.png"); int w = i.width; int h = i.height; PVector[][] d1 = new PVector[(int)(w / th) + 1][(int)(h / th) + 1]; for(int x = 0; x < d1.length; x++) { for(int y = 0; y < d1[0].length; y++) { int tmp1 = (int)random(x * th, (x + 1) * th); tmp1 = constrain(tmp1, 0, w - 1); int tmp2 = (int)random(y * th, (y + 1) * th); tmp2 = constrain(tmp2, 0, h - 1); d1[x][y] = new PVector(tmp1, tmp2); } } if(b) { float[][] r = new float[(int)(w / th) + 1][(int)(h / th) + 1]; float[][] g = new float[(int)(w / th) + 1][(int)(h / th) + 1]; float[][] b = new float[(int)(w / th) + 1][(int)(h / th) + 1]; float[][] n = new float[(int)(w / th) + 1][(int)(h / th) + 1]; for(int x = 0; x < d1.length; x++) { for(int y = 0; y < d1[0].length; y++) { int tmp1 = (int)random(x * th, (x + 1) * th); tmp1 = constrain(tmp1, 0, w - 1); int tmp2 = (int)random(y * th, (y + 1) * th); tmp2 = constrain(tmp2, 0, h - 1); d1[x][y] = new PVector(tmp1, tmp2); } } PVector[][] index1 = new PVector[w][h]; for(int x1 = 0; x1 < w; x1++) { for(int y1 = 0; y1 < h; y1++) { int tmp1 = (int)(x1 / th); int tmp2 = (int)(y1 / th); float[][] dist = new float[3][3]; PVector[][] index2 = new PVector[3][3]; for(int x2 = -1; x2 < 1 + 1; x2++) { for(int y2 = -1; y2 < 1 + 1; y2++) { if(tmp1 + x2 < 0 || tmp2 + y2 < 0 || tmp1 + x2 > d1.length - 1 || tmp2 + y2 > d1[0].length - 1) { dist[x2 + 1][y2 + 1] = -1; } else { dist[x2 + 1][y2 + 1] = dist(x1, y1, d1[tmp1 + x2][tmp2 + y2].x, d1[tmp1 + x2][tmp2 + y2].y); index2[x2 + 1][y2 + 1] = new PVector(tmp1 + x2, tmp2 + y2); } } } float min_dist = Float.MAX_VALUE; PVector index_min_dist = new PVector(); for(int x2 = 0; x2 < 3; x2++) { for(int y2 = 0; y2 < 3; y2++) { if(dist[x2][y2] != -1 && min_dist > dist[x2][y2]) { min_dist = dist[x2][y2]; index_min_dist = new PVector(x2, y2); } } } index1[x1][y1] = index2[(int)index_min_dist.x][(int)index_min_dist.y]; } } for(int x = 0; x < w; x++) { for(int y = 0; y < h; y++) { color c = i.get(x, y); r[(int)index1[x][y].x][(int)index1[x][y].y] += red(c); g[(int)index1[x][y].x][(int)index1[x][y].y] += green(c); b[(int)index1[x][y].x][(int)index1[x][y].y] += blue(c); n[(int)index1[x][y].x][(int)index1[x][y].y] += 1; } } for(int x = 0; x < w; x++) { for(int y = 0; y < h; y++) { color c1 = color(r[(int)index1[x][y].x][(int)index1[x][y].y] / n[(int)index1[x][y].x][(int)index1[x][y].y], g[(int)index1[x][y].x][(int)index1[x][y].y] / n[(int)index1[x][y].x][(int)index1[x][y].y], b[(int)index1[x][y].x][(int)index1[x][y].y] / n[(int)index1[x][y].x][(int)index1[x][y].y]); i.set(x, y, c1); } } } else { for(int x1 = 0; x1 < w; x1++) { for(int y1 = 0; y1 < h; y1++) { int tmp1 = (int)(x1 / th); int tmp2 = (int)(y1 / th); float[][] dist = new float[3][3]; PVector[][] d2 = new PVector[3][3]; for(int x2 = -1; x2 < 1 + 1; x2++) { for(int y2 = -1; y2 < 1 + 1; y2++) { if(tmp1 + x2 < 0 || tmp2 + y2 < 0 || tmp1 + x2 > d1.length - 1 || tmp2 + y2 > d1[0].length - 1) { dist[x2 + 1][y2 + 1] = -1; } else { dist[x2 + 1][y2 + 1] = dist(x1, y1, d1[tmp1 + x2][tmp2 + y2].x, d1[tmp1 + x2][tmp2 + y2].y); d2[x2 + 1][y2 + 1] = d1[tmp1 + x2][tmp2 + y2]; } } } float min_dist = Float.MAX_VALUE; PVector index_min_dist = new PVector(); for(int x2 = 0; x2 < 3; x2++) { for(int y2 = 0; y2 < 3; y2++) { if(dist[x2][y2] != -1 && min_dist > dist[x2][y2]) { min_dist = dist[x2][y2]; index_min_dist = new PVector(x2, y2); } } } color c = i.get((int)d2[(int)index_min_dist.x][(int)index_min_dist.y].x, (int)d2[(int)index_min_dist.x][(int)index_min_dist.y].y); i.set(x1, y1, c); } } } i.save("出力画像.png"); }