IDL,自适应细化
该方法的灵感来自于天文学模拟以及细分曲面的自适应网格细化。这是IDL引以为豪的任务,您可以通过我能够使用的大量内置函数来说明这一点。:D
我已经输出了黑色背景yoshi测试图像的一些中间物n = 1000
。
首先,我们在图像上执行亮度灰度(使用ct_luminance
),并应用Prewitt过滤器(prewitt
,请参阅Wikipedia)进行边缘检测:
然后是真正的艰苦工作:我们将图像细分为4,并测量滤波图像中每个象限的方差。我们的方差由细分的大小(在第一步中相等)加权,这样方差大的“前卫”区域就不会越来越小。然后,我们使用加权方差更详细地定位细分,然后将每个细节丰富的部分迭代细分为4个附加细分,直到达到目标站点数(每个细分恰好包含一个站点)。由于每次迭代时我们都会添加3个站点,因此最终会获得n - 2 <= N <= n
站点。
我为该图像创建了一个.webm细分过程,我无法嵌入它,但是它在这里;每个小节中的颜色由加权方差确定。(为了比较,我为白色背景的yoshi制作了相同类型的视频,但色表相反,所以它趋向于白色而不是黑色;它在这里。)细分的最终产品如下所示:
有了细分列表后,我们将遍历每个细分。最终的位置是Prewitt图像的最小值(即最小的“前卫”像素)的位置,截面的颜色是该像素的颜色;这是原始图片,带有标记的网站:
然后,在将每个多边形绘制到各自颜色的图像缓冲区之前,我们使用内置函数triangulate
来计算站点的Delaunay三角剖分,并使用内置voronoi
函数来定义每个Voronoi多边形的顶点。最后,我们保存图像缓冲区的快照。
编码:
function subdivide, image, bounds, vars
;subdivide a section into 4, and return the 4 subdivisions and the variance of each
division = list()
vars = list()
nx = bounds[2] - bounds[0]
ny = bounds[3] - bounds[1]
for i=0,1 do begin
for j=0,1 do begin
x = i * nx/2 + bounds[0]
y = j * ny/2 + bounds[1]
sub = image[x:x+nx/2-(~(nx mod 2)),y:y+ny/2-(~(ny mod 2))]
division.add, [x,y,x+nx/2-(~(nx mod 2)),y+ny/2-(~(ny mod 2))]
vars.add, variance(sub) * n_elements(sub)
endfor
endfor
return, division
end
pro voro_map, n, image, outfile
sz = size(image, /dim)
;first, convert image to greyscale, and then use a Prewitt filter to pick out edges
edges = prewitt(reform(ct_luminance(image[0,*,*], image[1,*,*], image[2,*,*])))
;next, iteratively subdivide the image into sections, using variance to pick
;the next subdivision target (variance -> detail) until we've hit N subdivisions
subdivisions = subdivide(edges, [0,0,sz[1],sz[2]], variances)
while subdivisions.count() lt (n - 2) do begin
!null = max(variances.toarray(),target)
oldsub = subdivisions.remove(target)
newsub = subdivide(edges, oldsub, vars)
if subdivisions.count(newsub[0]) gt 0 or subdivisions.count(newsub[1]) gt 0 or subdivisions.count(newsub[2]) gt 0 or subdivisions.count(newsub[3]) gt 0 then stop
subdivisions += newsub
variances.remove, target
variances += vars
endwhile
;now we find the minimum edge value of each subdivision (we want to pick representative
;colors, not edge colors) and use that as the site (with associated color)
sites = fltarr(2,n)
colors = lonarr(n)
foreach sub, subdivisions, i do begin
slice = edges[sub[0]:sub[2],sub[1]:sub[3]]
!null = min(slice,target)
sxy = array_indices(slice, target) + sub[0:1]
sites[*,i] = sxy
colors[i] = cgcolor24(image[0:2,sxy[0],sxy[1]])
endforeach
;finally, generate the voronoi map
old = !d.NAME
set_plot, 'Z'
device, set_resolution=sz[1:2], decomposed=1, set_pixel_depth=24
triangulate, sites[0,*], sites[1,*], tr, connectivity=C
for i=0,n-1 do begin
if C[i] eq C[i+1] then continue
voronoi, sites[0,*], sites[1,*], i, C, xp, yp
cgpolygon, xp, yp, color=colors[i], /fill, /device
endfor
!null = cgsnapshot(file=outfile, /nodialog)
set_plot, old
end
pro wrapper
cd, '~/voronoi'
fs = file_search()
foreach f,fs do begin
base = strsplit(f,'.',/extract)
if base[1] eq 'png' then im = read_png(f) else read_jpeg, f, im
voro_map,100, im, base[0]+'100.png'
voro_map,500, im, base[0]+'500.png'
voro_map,1000,im, base[0]+'1000.png'
endforeach
end
通过致电voro_map, n, image, output_filename
。我还包括了一个wrapper
过程,该过程遍历了每个测试图像并运行了100、500和1000个站点。
这里收集了输出,下面是一些缩略图:
n = 100
n = 500
n = 1000