消除使用Python图像库抗锯齿时透明边缘的细边框

6 投票
1 回答
2555 浏览
提问于 2025-04-16 17:32

我有一张高分辨率的图片,想用它作为谷歌地图API v3的平铺地图覆盖。

我使用MapTiler把这张图片切割成适合的瓦片,按照我想要的缩放级别。这一步做得挺好,但在原始图片的边缘上出现了一条细细的灰黑色边框。

根据第二个帖子里的建议,我尝试使用gdal2tiles.py,并加上了-r antialias这个选项,但那条细边框还是没消失。

如果我打开实际生成的图片瓦片,确实可以看到那条细边框是生成的瓦片的一部分,但它并不是原始图片的一部分。

我猜测发生的情况是,谷歌地图瓦片中没有数据的部分在生成瓦片图片文件时被当作黑色像素处理,结果就出现了灰色边框。

这是我认为与gdal2tiles.py相关的代码:

                    # Scaling by PIL (Python Imaging Library) - improved Lanczos
                    array = numpy.zeros((querysize, querysize, tilebands), numpy.uint8)
                    for i in range(tilebands):
                            array[:,:,i] = gdalarray.BandReadAsArray(dsquery.GetRasterBand(i+1), 0, 0, querysize, querysize)
                    im = Image.fromarray(array, 'RGBA') # Always four bands
                    im1 = im.resize((tilesize,tilesize), Image.ANTIALIAS)
                    if os.path.exists(tilefilename):
                            im0 = Image.open(tilefilename)
                            im1 = Image.composite(im1, im0, im1) 
                    im1.save(tilefilename,self.tiledriver)

有没有什么办法可以去掉这个边框,而不需要打开生成的瓦片图片文件,在图像编辑器里把相关像素设置为透明呢?

我觉得答案可能涉及到找到一种方法来表示透明像素,这样抗锯齿处理在采样时就能忽略它们。

更新:虽然这可能在优雅性或性能上不算出色,但我觉得我已经接近解决方案了。当然,最后的几步也是最难的。

如果我把ANTIALIAS改成BICUBIC,然后处理alpha通道,使得任何半透明的像素都变得完全透明,这样大部分瓦片的边框就消失了。不过,还是有一些浅色的边框残留。我不太确定该怎么处理这个问题。另外,我还想提到,如果图片中有透明或半透明的像素,而这些像素并不在实际图片区域的边缘,这个策略可能效果就不好了。

这是修改后的代码:

                # Scaling by PIL (Python Imaging Library) - improved Lanczos
                array = numpy.zeros((querysize, querysize, tilebands), numpy.uint8)
                for i in range(tilebands):
                        array[:,:,i] = gdalarray.BandReadAsArray(dsquery.GetRasterBand(i+1), 0, 0, querysize, querysize)
                im = Image.fromarray(array, 'RGBA') # Always four bands
                im1 = im.resize((tilesize,tilesize), Image.BICUBIC)
                if os.path.exists(tilefilename):
                        im0 = Image.open(tilefilename)
                        im1 = Image.composite(im1, im0, im1)
                im1AsArray = numpy.array(im1)
                alpha = im1AsArray[:,:,3]
                semiTransparentIndices = alpha < 255
                alpha[semiTransparentIndices] = 0
                im1AsArray[:,:,3] = alpha
                im1 = Image.fromarray(im1AsArray, 'RGBA')
                im1.save(tilefilename,self.tiledriver)

1 个回答

2

答案是把重采样方式改成BILINEAR(而不是我在问题更新中尝试的BICUBIC),然后确保把任何半透明的像素改成完全透明的像素。

正如我在更新中提到的,我这里做的代码修改可能不够优雅或高效,但确实能解决问题。下面是需要修改的gdal2tiles.py中原始代码片段:

            # Scaling by PIL (Python Imaging Library) - improved Lanczos
            array = numpy.zeros((querysize, querysize, tilebands), numpy.uint8)
            for i in range(tilebands):
                    array[:,:,i] = gdalarray.BandReadAsArray(dsquery.GetRasterBand(i+1), 0, 0, querysize, querysize)
            im = Image.fromarray(array, 'RGBA') # Always four bands
            im1 = im.resize((tilesize,tilesize), Image.BILINEAR)
            if os.path.exists(tilefilename):
                    im0 = Image.open(tilefilename)
                    im1 = Image.composite(im1, im0, im1)
            im1AsArray = numpy.array(im1)
            alpha = im1AsArray[:,:,3]
            semiTransparentIndices = alpha < 255
            alpha[semiTransparentIndices] = 0
            im1AsArray[:,:,3] = alpha
            im1 = Image.fromarray(im1AsArray, 'RGBA')
            im1.save(tilefilename,self.tiledriver)

另外,请注意,上面的代码只有在你给gdal2tiles.py传递-r antialias这个参数时才会执行。没错,我们把-r antialias的代码改了,所以它现在不再进行抗锯齿处理了。不过,如果你遇到我之前遇到的问题,只想要一个解决方案,那就可以用这个。

撰写回答