You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将手绘栅格TIFF地图转为平滑矢量中心线Shapefile

解决手绘灰度TIFF转平滑Shapefile的细碎多边形问题

问题根源

手绘地图的灰度TIFF天生存在像素不均、线条边缘毛刺、局部细碎噪点的问题,直接用gdal.Polygonize会把每个灰度波动的小区域都生成独立多边形——电脑绘制的图线条规整,自然不会有这麻烦。必须先做图像预处理把线条捋顺,再换合适的矢量化方法,不能直接硬转。

第一步:图像预处理(用OpenCV+GDAL)

先把手绘TIFF转成清晰的二值化单线条,消掉噪点和平滑毛刺:

  • 二值化:把灰度图切成黑白两色,只保留深色的手绘线条,过滤浅灰背景噪点。
  • 形态学降噪:先腐蚀掉细碎的小噪点,再膨胀补回线条的正常厚度,让线条更连贯。
  • 平滑处理:用高斯模糊弱化线条边缘的锯齿感。

预处理代码示例

import cv2
import numpy as np
from osgeo import gdal
import os

# 读取原TIFF并保留地理信息
ds = gdal.Open('input_image3.tif')
raster = ds.ReadAsArray()
geo_transform = ds.GetGeoTransform()
projection = ds.GetProjection()
ds = None

# 1. 二值化:阈值根据手绘线条深浅调整,偏暗就调低127
_, binary = cv2.threshold(raster, 127, 255, cv2.THRESH_BINARY_INV)

# 2. 形态学操作:先腐蚀消噪,再膨胀补全线条
kernel = np.ones((3, 3), np.uint8)
binary = cv2.erode(binary, kernel, iterations=1)
binary = cv2.dilate(binary, kernel, iterations=1)

# 3. 高斯模糊平滑线条边缘
binary = cv2.GaussianBlur(binary, (3, 3), 0)

# 保存预处理后的TIFF,保留原地理坐标
driver = gdal.GetDriverByName('GTiff')
out_path = 'preprocessed.tif'
if os.path.exists(out_path):
    driver.DeleteDataSource(out_path)
out_ds = driver.Create(out_path, binary.shape[1], binary.shape[0], 1, gdal.GDT_Byte)
out_ds.SetGeoTransform(geo_transform)
out_ds.SetProjection(projection)
out_band = out_ds.GetRasterBand(1)
out_band.WriteArray(binary)
out_band.FlushCache()
out_ds = None

第二步:正确矢量化(生成平滑线条)

原来的gdal.Polygonize是用来生成多边形的,直接放到LineString图层会导致逻辑错误。应该用gdal.TraceContours直接追踪线条轮廓,再做简化处理:

矢量化代码示例

from osgeo import ogr, gdal, osr
import os

input_tiff = 'preprocessed.tif'
output_shapefile = 'outputs/smooth_lines.shp'

# 读取预处理后的TIFF
ds = gdal.Open(input_tiff)
band = ds.GetRasterBand(1)
spatial_ref = osr.SpatialReference(wkt=ds.GetProjection())

# 创建输出Shapefile,先删除已有文件
driver = ogr.GetDriverByName('ESRI Shapefile')
if os.path.exists(output_shapefile):
    driver.DeleteDataSource(output_shapefile)
shapefile = driver.CreateDataSource(output_shapefile)

# 创建LineString类型的图层
layer = shapefile.CreateLayer('lines', srs=spatial_ref, geom_type=ogr.wkbLineString)
# 添加ID字段(可选)
layer.CreateField(ogr.FieldDefn('ID', ogr.OFTInteger))

# 追踪二值图的黑色线条轮廓(阈值设为0)
gdal.TraceContours(band, 0, 0, layer, 0, 0, callback=None)

# 用Douglas-Peucker算法简化线条,减少细碎顶点
feature = layer.GetNextFeature()
while feature:
    geom = feature.GetGeometryRef()
    # 简化阈值:单位与地图坐标一致,值越大线条越平滑,按需调整
    simplified_geom = geom.Simplify(1.0)
    feature.SetGeometry(simplified_geom)
    layer.SetFeature(feature)
    feature = layer.GetNextFeature()

# 清理资源
ds = None
shapefile = None
print("平滑线条转换完成")

关键调参提示

  • 二值化阈值:手绘线条偏浅就调低127,背景噪点多就调高,确保只保留目标线条。
  • 形态学核大小:线条细用(2,2)的核,线条粗用(3,3),避免把线条弄断。
  • 简化阈值:Simplify(1.0)的数值是坐标单位(比如米制地图就是1米误差),根据精度需求调整。
  • 地理信息保留:预处理时必须复制原TIFF的地理变换和投影,否则Shapefile会出现坐标偏移。

内容的提问来源于stack exchange,提问作者Saddam Hussain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:52:53