将二值图转化成signed distance field后,可以在双线性插值下实现平滑放大。

定义:

到前景的distance field:各点到最近前景点的距离。

到背景的distance field:各点到最近背景景点的距离。

则: signed distance field = 到背景的distance field - 到前景的distance field。

注:最好严格按上面定义计算signed distance field。看到有的博文中说先提取轮廓点,然后计算各点到最近轮廓点的距离,并且如果此点是前景点就将计算出的距离加+号,如果此点是背景点就将计算出的距离加-号。这样确实也得到一个signed distance field,但显然这样计算出来的signed distance field跟严格按照上面定义计算出来的结果是不一样的,对结果准确性是否造成影响不太清楚。

若按前面标准定义计算出signed distance field后,轮廓阈值应取为0,即signed distance field中大于等于0的像素复原为前景。

实际存储的时候我是求了一下signed distance field中的最大值max和最小值min,然后通过(signedDis-min)/(max-min)将signedDis映射到[0,1],并且将轮廓阈值0映射为(0-min)/(max-min),即得到了一个取值在[0,1]间的signed distance field,其轮廓阈值为(0-min)/(max-min)。

生成signed distance field的算法,开始我在这个博文(http://blog.csdn.net/tianwaifeimao/article/details/45078661)中找到一个Saito算法,它利用距离平方在x和y上可分开处理的性质提高了计算效率,虽然没有完全达到线性复杂度,但也比暴力算法快得多。算法的正确性很容易看出来,实现出来实测了一下,也没问题。

后来又在网上找到一个称为8ssedt的算法(见:http://www.codersnotes.com/algorithms/signed-distance-fields),博文中给的论文链接打不开,但给出源代码下载,代码很短能看明白,用的是与最短路径的算法相同的思路,针对问题本身的结构做了很巧妙的优化,达到了线性复杂度。(注:前述Saito算法第一步求各点在本行中的最近前景点时也可以利用8ssedt算法的思路进行优化计算)。

8ssedt算法代码如下(转自:http://www.codersnotes.com/algorithms/signed-distance-fields):

#include "SDL/sdl.h"
#include <math.h> #define WIDTH 256
#define HEIGHT 256 struct Point
{
int dx, dy; int DistSq() const { return dx*dx + dy*dy; }
}; struct Grid
{
Point grid[HEIGHT][WIDTH];
}; Point inside = { 0, 0 };
Point empty = { 9999, 9999 };
Grid grid1, grid2; Point Get( Grid &g, int x, int y )
{
// OPTIMIZATION: you can skip the edge check code if you make your grid
// have a 1-pixel gutter.
if ( x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT )
return g.grid[y][x];
else
return empty;
} void Put( Grid &g, int x, int y, const Point &p )
{
g.grid[y][x] = p;
} void Compare( Grid &g, Point &p, int x, int y, int offsetx, int offsety )
{
Point other = Get( g, x+offsetx, y+offsety );
other.dx += offsetx;
other.dy += offsety; if (other.DistSq() < p.DistSq())
p = other;
} void GenerateSDF( Grid &g )
{
// Pass 0
for (int y=0;y<HEIGHT;y++)
{
for (int x=0;x<WIDTH;x++)
{
Point p = Get( g, x, y );
Compare( g, p, x, y, -1, 0 );
Compare( g, p, x, y, 0, -1 );
Compare( g, p, x, y, -1, -1 );
Compare( g, p, x, y, 1, -1 );
Put( g, x, y, p );
} for (int x=WIDTH-1;x>=0;x--)
{
Point p = Get( g, x, y );
Compare( g, p, x, y, 1, 0 );
Put( g, x, y, p );
}
} // Pass 1
for (int y=HEIGHT-1;y>=0;y--)
{
for (int x=WIDTH-1;x>=0;x--)
{
Point p = Get( g, x, y );
Compare( g, p, x, y, 1, 0 );
Compare( g, p, x, y, 0, 1 );
Compare( g, p, x, y, -1, 1 );
Compare( g, p, x, y, 1, 1 );
Put( g, x, y, p );
} for (int x=0;x<WIDTH;x++)
{
Point p = Get( g, x, y );
Compare( g, p, x, y, -1, 0 );
Put( g, x, y, p );
}
}
} int main( int argc, char* args[] )
{
if ( SDL_Init( SDL_INIT_VIDEO ) == -1 )
return 1; SDL_Surface *screen = SDL_SetVideoMode( WIDTH, HEIGHT, 32, SDL_SWSURFACE );
if ( !screen )
return 1; // Initialize the grid from the BMP file.
SDL_Surface *temp = SDL_LoadBMP( "test.bmp" );
temp = SDL_ConvertSurface( temp, screen->format, SDL_SWSURFACE );
SDL_LockSurface( temp );
for( int y=0;y<HEIGHT;y++ )
{
for ( int x=0;x<WIDTH;x++ )
{
Uint8 r,g,b;
Uint32 *src = ( (Uint32 *)( (Uint8 *)temp->pixels + y*temp->pitch ) ) + x;
SDL_GetRGB( *src, temp->format, &r, &g, &b ); // Points inside get marked with a dx/dy of zero.
// Points outside get marked with an infinitely large distance.
if ( g < 128 )
{
Put( grid1, x, y, inside );
Put( grid2, x, y, empty );
} else {
Put( grid2, x, y, inside );
Put( grid1, x, y, empty );
}
}
}
SDL_UnlockSurface( temp ); // Generate the SDF.
GenerateSDF( grid1 );
GenerateSDF( grid2 ); // Render out the results.
SDL_LockSurface( screen );
for( int y=0;y<HEIGHT;y++ )
{
for ( int x=0;x<WIDTH;x++ )
{
// Calculate the actual distance from the dx/dy
int dist1 = (int)( sqrt( (double)Get( grid1, x, y ).DistSq() ) );
int dist2 = (int)( sqrt( (double)Get( grid2, x, y ).DistSq() ) );
int dist = dist1 - dist2; // Clamp and scale it, just for display purposes.
int c = dist*3 + 128;
if ( c < 0 ) c = 0;
if ( c > 255 ) c = 255; Uint32 *dest = ( (Uint32 *)( (Uint8 *)screen->pixels + y*screen->pitch ) ) + x;
*dest = SDL_MapRGB( screen->format, c, c, c );
}
}
SDL_UnlockSurface( screen );
SDL_Flip( screen ); // Wait for a keypress
SDL_Event event;
while( true )
{
if ( SDL_PollEvent( &event ) )
switch( event.type )
{
case SDL_QUIT:
case SDL_KEYDOWN:
return true;
}
} return 0;
}

signed distance field 算法的更多相关文章

  1. Signed Distance Field Shadow in Unity

    0x00 前言 最近读到了一个今年GDC上很棒的分享,是Sebastian Aaltonen带来的利用Ray-tracing实现一些有趣的效果的分享. 其中有一段他介绍到了对Signed Distan ...

  2. Signed Distance Field Technique

    [Distance Field Technique] 一种小纹理高清放大的技术. A distance field is generated from a high resolution image, ...

  3. distance field(占坑

    signed distance field https://kosmonautblog.wordpress.com/2017/05/09/signed-distance-field-rendering ...

  4. 扒一扒编辑距离(Levenshtein Distance)算法

    最近由于工作需要,接触了编辑距离(Levenshtein Distance)算法.赶脚很有意思.最初百度了一些文章,但讲的都不是很好,读起来感觉似懂非懂.最后还是用google找到了一些资料才慢慢理解 ...

  5. Levenshtein distance 编辑距离算法

    这几天再看 virtrual-dom,关于两个列表的对比,讲到了 Levenshtein distance 距离,周末抽空做一下总结. Levenshtein Distance 介绍 在信息理论和计算 ...

  6. Lucene的FuzzyQuery中用到的Levenshtein Distance(LD)算法

    2019独角兽企业重金招聘Python工程师标准>>> Lucene的FuzzyQuery中用到的Levenshtein Distance(LD)算法 博客分类: java 搜索引擎 ...

  7. hdu 4712 Hamming Distance ( 随机算法混过了 )

    Hamming Distance Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) ...

  8. Levenshtein Distance (编辑距离) 算法详解

    编辑距离即从一个字符串变换到另一个字符串所需要的最少变化操作步骤(以字符为单位,如son到sun,s不用变,将o->s,n不用变,故操作步骤为1). 为了得到编辑距离,我们画一张二维表来理解,以 ...

  9. Levenshtein Distance + LCS 算法计算两个字符串的相似度

    //LD最短编辑路径算法 public static int LevenshteinDistance(string source, string target) { int cell = source ...

随机推荐

  1. 使用hosts.allow和hosts.deny实现简单的防火墙

    说明:我建议学习防火墙只单一学习一种就够了,这种方式虽然简单和快速,但也有些不太灵活,所以如果要深入防火墙建议转iptables  一.背景简介 在Linux上多用iptables来限制ssh和tel ...

  2. datetimepicker只显示日期,不显示时分秒

    HTML代码<div class="input-group date form_datetime form-date" data-link-field="dtp_i ...

  3. VB里的TEXT控件只能输入数字的代码

    " '先声明一个常量,并把你想禁用或允许输入的内容赋值给它 Private Sub Text1_KeyPress(KeyAscii As Integer) '只能输入数字 KeyAscii ...

  4. Multivariate Adaptive Regression Splines (MARSplines)

    Introductory Overview Regression Problems Multivariate Adaptive Regression Splines Model Selection a ...

  5. 拍拍CPS入门使用

    1.新建应用,获取应用相应的appOAuthID.appOAuthKey.accessToken(这个一点击获取就会改变的,而且最长有效期为3个月,失效了需要重新获取) http://fuwu.pai ...

  6. 关于TagHelper的那些事情——TagHelper的基本知识

    概要 TagHelper是ASP.NET 5的一个新特性.也许在你还没有听说过它的时候, 它已经在技术人员之间引起了大量讨论,甚至有一部分称它为服务器控件的回归.实际上它只不过是一个简化版本,把HTM ...

  7. flask使用ajax上传图片或者文件

    function upload_cover(){ var cover = new FormData(); var fileObj = document.getElementById('cover'). ...

  8. 流畅的python第九章符合Python风格的对象学习记录

    对象表示形式 每门面向对象的语言至少都有一种获取对象的字符串表示形式的标准方式.Python提供了两种方式 repr()便于开发者理解的方式返回对象的字符串表示形式 str()便于用户理解的方式返回对 ...

  9. 纯CSS实现多行文字垂直居中几种方法解析

    场景:父元素 高度固定,如何使其中的文字垂直居中? 1.table布局: 利用display:table+display:table-cell的方法 <!DOCTYPE html> < ...

  10. php fopen函数返回false

    使用yum安装的apache2.4.6   php 7.2.2  正确安装后使用fopen()函数打开文件时返回false 百度一下又三种原因 1.目录或者文件夹权限原因 (可以用   chmod - ...