我正在编写一个小型实用程序来计算 signed distance field图形应用程序的纹理。我正在做真正的有符号距离场,而不是近似值,所以我首先将每个字形路径转换为圆弧样条,以加速点到路径距离计算。问题是我在某些角落出现了奇怪的伪影:
路径是从 EPS 中提取的由 FontForge 生成无需任何操纵。计算距离,找到从每个像素坐标到任何路径线段或弧的最小距离(三个嵌套循环: for (x;...) { for (y; ...) { for (i; ...) { ... }}}
)。迭代计算的每像素距离以提取最小值和最大值,并重新调整为 0-255 范围,直接写入原始图像文件并转换为 PNG与 ImageMagick .
我认为这个错误的唯一来源是用于计算点到段距离的函数内部的数值错误。这是:
double dist_to_segment(double px, double py, /* query point */
double x0, double y0, /* first segment end-point */
double x1, double y1) /* second segment end-point */
{
const double t0 = dist2(x0, y0, x1, y1);
if (t0 == 0.0) { return dist2(px, py, x0, x1); }
const double t1 = dot(px-x0, py-y0, x1-x0, y1-y0)/t0;
const double t2 = clamp(t1, 0.0, 1.0);
const double t3 = sqrt(dist2(px, py, lerp(x0,x1,t2), lerp(y0,y1,t2)));
const double t4 = (x1-x0)*(py-y0) - (y1-y0)*(px-x0);
return (t4 < 0.0)? -t3 : t3;
}
哪里dot
, clamp
, lerp
定义为 OpenGL 着色语言和 dist2
定义为:
double dist2(double x0, double y0, double x1, double y1)
{
return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1);
}
如果我替换return (t4 < 0.0)? -t3 : t3;
与 return t3;
上dist_to_segment
我得到这个无符号距离场:
编辑
我通过向现有的边缘迭代循环添加多边形内的点测试来解决了小的三角形工件,因此额外的成本并不太高。不过,沿锐角平分线的锋利特征。有新的示例图像。
最佳答案
这是一个扩展注释,展示如何使用 netpbm 工具将灰度级转换为轮廓。从OP的最后一张图片来看,这会产生
以下 Bash 脚本使用 ppmchange
将确切的颜色值重新映射到由白色分隔的色带:
#!/bin/bash
colormap=()
for ((i = 0; i < 256; i++)); do
colormap+=( $(printf '#%02x%02x%02x' $i $i $i) )
if (( (i & 15) < 6 )); then
colormap+=( $(printf '#%02x00%02x' $[(i/16)*17] $[255-(i/16)*17]) )
else
colormap+=( "#ffffff" )
fi
done
exec ppmchange -closeness 0 ${colormap[@]} "$@"
我喜欢称之为灰色到轮廓
。如果你想指定确切的颜色,你可以使用
#!/bin/sh
exec ppmchange -closeness 0 \
'#000000' '#0000ff' \
'#010101' '#0000ff' \
'#020202' '#0000ff' \
'#030303' '#0000ff' \
'#040404' '#0000ff' \
'#050505' '#0000ff' \
'#060606' '#ffffff' \
'#070707' '#ffffff' \
'#080808' '#ffffff' \
'#090909' '#ffffff' \
'#0a0a0a' '#ffffff' \
'#0b0b0b' '#ffffff' \
'#0c0c0c' '#ffffff' \
'#0d0d0d' '#ffffff' \
'#0e0e0e' '#ffffff' \
'#0f0f0f' '#ffffff' \
'#101010' '#1100ee' \
'#111111' '#1100ee' \
'#121212' '#1100ee' \
'#131313' '#1100ee' \
'#141414' '#1100ee' \
'#151515' '#1100ee' \
'#161616' '#ffffff' \
'#171717' '#ffffff' \
'#181818' '#ffffff' \
'#191919' '#ffffff' \
'#1a1a1a' '#ffffff' \
'#1b1b1b' '#ffffff' \
'#1c1c1c' '#ffffff' \
'#1d1d1d' '#ffffff' \
'#1e1e1e' '#ffffff' \
'#1f1f1f' '#ffffff' \
'#202020' '#2200dd' \
'#212121' '#2200dd' \
'#222222' '#2200dd' \
'#232323' '#2200dd' \
'#242424' '#2200dd' \
'#252525' '#2200dd' \
'#262626' '#ffffff' \
'#272727' '#ffffff' \
'#282828' '#ffffff' \
'#292929' '#ffffff' \
'#2a2a2a' '#ffffff' \
'#2b2b2b' '#ffffff' \
'#2c2c2c' '#ffffff' \
'#2d2d2d' '#ffffff' \
'#2e2e2e' '#ffffff' \
'#2f2f2f' '#ffffff' \
'#303030' '#3300cc' \
'#313131' '#3300cc' \
'#323232' '#3300cc' \
'#333333' '#3300cc' \
'#343434' '#3300cc' \
'#353535' '#3300cc' \
'#363636' '#ffffff' \
'#373737' '#ffffff' \
'#383838' '#ffffff' \
'#393939' '#ffffff' \
'#3a3a3a' '#ffffff' \
'#3b3b3b' '#ffffff' \
'#3c3c3c' '#ffffff' \
'#3d3d3d' '#ffffff' \
'#3e3e3e' '#ffffff' \
'#3f3f3f' '#ffffff' \
'#404040' '#4400bb' \
'#414141' '#4400bb' \
'#424242' '#4400bb' \
'#434343' '#4400bb' \
'#444444' '#4400bb' \
'#454545' '#4400bb' \
'#464646' '#ffffff' \
'#474747' '#ffffff' \
'#484848' '#ffffff' \
'#494949' '#ffffff' \
'#4a4a4a' '#ffffff' \
'#4b4b4b' '#ffffff' \
'#4c4c4c' '#ffffff' \
'#4d4d4d' '#ffffff' \
'#4e4e4e' '#ffffff' \
'#4f4f4f' '#ffffff' \
'#505050' '#5500aa' \
'#515151' '#5500aa' \
'#525252' '#5500aa' \
'#535353' '#5500aa' \
'#545454' '#5500aa' \
'#555555' '#5500aa' \
'#565656' '#ffffff' \
'#575757' '#ffffff' \
'#585858' '#ffffff' \
'#595959' '#ffffff' \
'#5a5a5a' '#ffffff' \
'#5b5b5b' '#ffffff' \
'#5c5c5c' '#ffffff' \
'#5d5d5d' '#ffffff' \
'#5e5e5e' '#ffffff' \
'#5f5f5f' '#ffffff' \
'#606060' '#660099' \
'#616161' '#660099' \
'#626262' '#660099' \
'#636363' '#660099' \
'#646464' '#660099' \
'#656565' '#660099' \
'#666666' '#ffffff' \
'#676767' '#ffffff' \
'#686868' '#ffffff' \
'#696969' '#ffffff' \
'#6a6a6a' '#ffffff' \
'#6b6b6b' '#ffffff' \
'#6c6c6c' '#ffffff' \
'#6d6d6d' '#ffffff' \
'#6e6e6e' '#ffffff' \
'#6f6f6f' '#ffffff' \
'#707070' '#770088' \
'#717171' '#770088' \
'#727272' '#770088' \
'#737373' '#770088' \
'#747474' '#770088' \
'#757575' '#770088' \
'#767676' '#ffffff' \
'#777777' '#ffffff' \
'#787878' '#ffffff' \
'#797979' '#ffffff' \
'#7a7a7a' '#ffffff' \
'#7b7b7b' '#ffffff' \
'#7c7c7c' '#ffffff' \
'#7d7d7d' '#ffffff' \
'#7e7e7e' '#ffffff' \
'#7f7f7f' '#ffffff' \
'#808080' '#880077' \
'#818181' '#880077' \
'#828282' '#880077' \
'#838383' '#880077' \
'#848484' '#880077' \
'#858585' '#880077' \
'#868686' '#ffffff' \
'#878787' '#ffffff' \
'#888888' '#ffffff' \
'#898989' '#ffffff' \
'#8a8a8a' '#ffffff' \
'#8b8b8b' '#ffffff' \
'#8c8c8c' '#ffffff' \
'#8d8d8d' '#ffffff' \
'#8e8e8e' '#ffffff' \
'#8f8f8f' '#ffffff' \
'#909090' '#990066' \
'#919191' '#990066' \
'#929292' '#990066' \
'#939393' '#990066' \
'#949494' '#990066' \
'#959595' '#990066' \
'#969696' '#ffffff' \
'#979797' '#ffffff' \
'#989898' '#ffffff' \
'#999999' '#ffffff' \
'#9a9a9a' '#ffffff' \
'#9b9b9b' '#ffffff' \
'#9c9c9c' '#ffffff' \
'#9d9d9d' '#ffffff' \
'#9e9e9e' '#ffffff' \
'#9f9f9f' '#ffffff' \
'#a0a0a0' '#aa0055' \
'#a1a1a1' '#aa0055' \
'#a2a2a2' '#aa0055' \
'#a3a3a3' '#aa0055' \
'#a4a4a4' '#aa0055' \
'#a5a5a5' '#aa0055' \
'#a6a6a6' '#ffffff' \
'#a7a7a7' '#ffffff' \
'#a8a8a8' '#ffffff' \
'#a9a9a9' '#ffffff' \
'#aaaaaa' '#ffffff' \
'#ababab' '#ffffff' \
'#acacac' '#ffffff' \
'#adadad' '#ffffff' \
'#aeaeae' '#ffffff' \
'#afafaf' '#ffffff' \
'#b0b0b0' '#bb0044' \
'#b1b1b1' '#bb0044' \
'#b2b2b2' '#bb0044' \
'#b3b3b3' '#bb0044' \
'#b4b4b4' '#bb0044' \
'#b5b5b5' '#bb0044' \
'#b6b6b6' '#ffffff' \
'#b7b7b7' '#ffffff' \
'#b8b8b8' '#ffffff' \
'#b9b9b9' '#ffffff' \
'#bababa' '#ffffff' \
'#bbbbbb' '#ffffff' \
'#bcbcbc' '#ffffff' \
'#bdbdbd' '#ffffff' \
'#bebebe' '#ffffff' \
'#bfbfbf' '#ffffff' \
'#c0c0c0' '#cc0033' \
'#c1c1c1' '#cc0033' \
'#c2c2c2' '#cc0033' \
'#c3c3c3' '#cc0033' \
'#c4c4c4' '#cc0033' \
'#c5c5c5' '#cc0033' \
'#c6c6c6' '#ffffff' \
'#c7c7c7' '#ffffff' \
'#c8c8c8' '#ffffff' \
'#c9c9c9' '#ffffff' \
'#cacaca' '#ffffff' \
'#cbcbcb' '#ffffff' \
'#cccccc' '#ffffff' \
'#cdcdcd' '#ffffff' \
'#cecece' '#ffffff' \
'#cfcfcf' '#ffffff' \
'#d0d0d0' '#dd0022' \
'#d1d1d1' '#dd0022' \
'#d2d2d2' '#dd0022' \
'#d3d3d3' '#dd0022' \
'#d4d4d4' '#dd0022' \
'#d5d5d5' '#dd0022' \
'#d6d6d6' '#ffffff' \
'#d7d7d7' '#ffffff' \
'#d8d8d8' '#ffffff' \
'#d9d9d9' '#ffffff' \
'#dadada' '#ffffff' \
'#dbdbdb' '#ffffff' \
'#dcdcdc' '#ffffff' \
'#dddddd' '#ffffff' \
'#dedede' '#ffffff' \
'#dfdfdf' '#ffffff' \
'#e0e0e0' '#ee0011' \
'#e1e1e1' '#ee0011' \
'#e2e2e2' '#ee0011' \
'#e3e3e3' '#ee0011' \
'#e4e4e4' '#ee0011' \
'#e5e5e5' '#ee0011' \
'#e6e6e6' '#ffffff' \
'#e7e7e7' '#ffffff' \
'#e8e8e8' '#ffffff' \
'#e9e9e9' '#ffffff' \
'#eaeaea' '#ffffff' \
'#ebebeb' '#ffffff' \
'#ececec' '#ffffff' \
'#ededed' '#ffffff' \
'#eeeeee' '#ffffff' \
'#efefef' '#ffffff' \
'#f0f0f0' '#ff0000' \
'#f1f1f1' '#ff0000' \
'#f2f2f2' '#ff0000' \
'#f3f3f3' '#ff0000' \
'#f4f4f4' '#ff0000' \
'#f5f5f5' '#ff0000' \
'#f6f6f6' '#ffffff' \
'#f7f7f7' '#ffffff' \
'#f8f8f8' '#ffffff' \
'#f9f9f9' '#ffffff' \
'#fafafa' '#ffffff' \
'#fbfbfb' '#ffffff' \
'#fcfcfc' '#ffffff' \
'#fdfdfd' '#ffffff' \
'#fefefe' '#ffffff' \
'#ffffff' '#ffffff' "$@"
其中左侧对应256个灰度级中的每一个,右侧是对应的颜色。
如果原始图像是gray.png
,您可以使用它创建contour.png
pngtopnm gray.png | ./gray-to-contour | pnmtopng -compress 9 > contour.png
正如我在评论中提到的,我们人类将梯度的急剧变化视为边缘,而在 OP 的最终图像中,只是看起来角平分线太亮/太暗。我试图找到一些对该效果的引用,但这些术语现在就被我忽略了。
虽然灰度图像易于处理和使用,但在某些情况下,人类心理视觉的怪异现象会欺骗我们。出于这个原因,我个人确实会以灰度和轮廓形式查看凹凸贴图和距离场;在我看来,这两种表述是相辅相成的。
关于c - 有符号距离场计算错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43613256/