AutoCAD 影像自动地理配准工具:INSGEO
INSGEO.lsp在 AutoCAD 中按真实地理坐标插入栅格影像的 AutoLISP 脚本——开源、精准、批量。
简介
在 AutoCAD 里贴航拍图、正射影像或扫描地形图,常见做法有几种:手动量距离做缩放对齐(慢、不准);用 91 卫图助手、水经注等软件附带的 INSG 插件(INSG 最初是个人开发者共享出来的 vlx 插件,但它一次只能导入一张,几十张影像就要重复几十遍);或者上付费软件,如南方 CASS + CASS 3D 加载大体积影像。
INSGEO.lsp 是一个免费的 AutoLISP 脚本,一条命令 INSGEO 批量插入多张影像,根据每张的配准文件自动落在正确的世界坐标上。
三个特点:
- 开源免费 —— 单文件 LSP,BSD-3-Clause 协议,可读、可改;没有付费授权、没有黑盒 🎉
- 精准对齐 —— 严格按 World File / ERS 坐标变换公式实现:左上像素中心 → 左下原点的换算、ERS 任意配准点支持、像素尺寸与旋转角全部到位,不靠肉眼对齐 🎯
- 批量导入 —— 一次勾选 N 张影像,逐张自动找配套配准文件并定位,中间不需要任何交互 📦
支持的影像格式:TIF / JPG / PNG / ECW
支持的配准文件:.tfw / .jgw / .pgw(ESRI World File)与 .ers(ER Mapper 头文件)
下载
脚本已上传至百度网盘,分享链接:https://pan.baidu.com/s/147_ljYpuInSk4ebW_lFNRg?pwd=ualr
加载与使用
将 INSGEO.lsp 拖入 AutoCAD,或在命令行执行 (load "C:/path/to/INSGEO.lsp")。加载成功后命令行会提示:

输入 INSGEO 即可启动。脚本会弹出多文件选择对话框,左侧显示当前目录下符合扩展名的文件和子文件夹,可以双击进入子目录,选中文件后点 Add Files 添加到右侧"已选"列表,最终点确定:

确定后脚本会逐张插入,每张插入完成都会在命令行打印进度,例如:
Inserting image 1 of 3 ...
Inserting image 2 of 3 ...
Inserting image 3 of 3 ...
Done. 3 image(s) inserted.
整个流程只需两步:输入命令、勾选文件。如果当某张影像没有任何配套配准文件,脚本会弹出文件对话框让用户手动指定;若仍然取消,会留在原点并打印提示。
工作流与团队协作
INSGEO 解决的是"插入"这一步。但插入完成的 dwg 怎么和团队同事共享、怎么交付给客户、影像加载怎么不卡——这些是实际工作里马上会遇到的问题。这一节给出三种典型场景的标准做法。
dwg 的影像引用机制
需要先讲清楚一个前提:AutoCAD 的影像引用只在 dwg 里存了一条路径,影像本身不嵌入 dwg 文件。打开 dwg 时,AutoCAD 按这条路径去硬盘上找影像文件:
- 找到 → 显示影像
- 找不到 → 显示空白边框,中间标着图像名
所以"对方能不能看到影像"等价于"对方那台电脑按 dwg 里存的路径能不能找到影像文件"。下面三种场景对应"路径"的三种处理思路。
场景 A:个人使用 —— 本地路径就够了
dwg 始终在自己电脑上打开,影像放本地任何位置都行,无需特殊处理。
场景 B:团队协作 —— SMB 共享路径
把所有影像文件 + 配准文件放到团队 SMB 共享(局域网文件服务器、NAS,或者直接是某台主力机器开的共享),团队成员在自己电脑上 INSGEO 时直接引用共享路径。dwg 里存下来的引用是 UNC 路径,同事打开 dwg 时只要也能访问同一个共享,影像就直接显示,零配置。
推荐用计算机名形式的 UNC
也就是 \\HOSTNAME\share\... 而不是 \\192.168.x.x\share\...。原因是 DHCP 环境下服务器 IP 可能变化,IP 一变 dwg 里所有引用就全失效;而计算机名通常是稳定的。在不方便给共享主机分配静态 IP 的场景下,计算机名形式比 IP 形式更省心。
在 INSGEO 对话框的 Folder 框里直接粘计算机名 UNC 即可:

历史小注:LM:getfiles 原版用 AutoLISP 内置的
vl-directory-files列目录,这个函数在某些 AutoCAD 版本下对计算机名形式的 UNC 解析失败(返回空列表,对话框只显示..),导致以前必须用 IP 形式才能正常工作。INSGEO.lsp 已经把这部分换成了Scripting.FileSystemObject,计算机名 UNC 现在完全可用。如果你拿到其它项目的 LSP 在 UNC 路径下出现"列不出文件"的现象,可以参考本脚本第 5 节的修复做法。
几个常见踩坑点
- 凭据/权限:AutoCAD 用当前 Windows 用户的身份访问 SMB。共享如果设了密码或权限,Windows 资源管理器里需要先访问过一次,AutoCAD 才能跟着用
- 管理员模式:AutoCAD 如果是"以管理员身份运行"启动的,而 SMB 是普通用户挂载的——两个会话在 Windows 里隔离,AutoCAD 看不到共享。对策:不要给 AutoCAD 加"以管理员身份运行"
- 网络速度:从 SMB 加载几 GB 的航拍 tif 比本地慢,但金字塔(.ovr)能极大缓解,见下面"加载性能"一节
场景 C:对外交付 —— ETRANSMIT 打包
如果要把项目发给没有 SMB 访问权的外部客户,用 AutoCAD 自带的 ETRANSMIT 命令打包。它会自动收集 dwg 引用的所有外部文件(影像、字体、外参、打印样式),改写好相对路径,生成一个独立的 zip,对方解压后双击 dwg 即可正常显示所有影像。
操作步骤
打开 dwg,命令行输入 ETRANSMIT,弹出"创建传递"对话框,文件清单里能看到 dwg 加上它引用的影像 tif 等所有依赖:

点"传递设置"进入详细配置,几个值得留意的选项:

- 传递包类型:选
Zip (*.zip)—— 通用、不被杀软拦 - 文件格式:如果对方 AutoCAD 版本比你低,在这里降级保存(图中是
AutoCAD 2013/LT 2013 图形格式,兼容性最好) - 路径选项:选"使用整理后的文件夹结构"—— 解压后影像、字体等按子目录分类,清晰易读
- 包含选项:必勾"包含光栅图像文件",其它根据需要勾(包含字体能避免对方字体替换)
点确定生成 zip,发给客户即可。客户解压后打开 dwg,影像、配准、缩放全部正常。
加载性能:别忘了金字塔
大尺寸 tif 在 AutoCAD 里加载和缩放都慢。带内部金字塔或外部 .ovr 文件的 tif 在缩放时只读对应分辨率层,十倍以上加速。
iTwin Capture Modeler 等主流摄影测量软件默认会同时生成 .tif 和 .tif.ovr——前者是原图,后者是金字塔。两者必须一起拷贝:
- SMB 共享场景:tif 和 ovr 都放共享里,AutoCAD 自动找到 ovr,享受加速
- ETRANSMIT 场景:它自动把 ovr 也打包进去,无需手动打包
- 本地手动拷贝:记得连 ovr 一起带,只拷 tif 会丢金字塔
如果发现某张 tif 没有配套 ovr(用 gdalinfo 检查输出里有没有 Overviews: 一行),可以用 GDAL 一行命令补:
gdaladdo -r average <file.tif> 2 4 8 16 32
这会在 tif 同目录下生成 <file.tif>.ovr。OSGeo4W、QGIS 都自带 GDAL,装一个就有命令行可用。
核心实现解析
如果你只是用脚本,本节可以跳过;以下面向想读懂或修改脚本的开发者。
脚本按职责分成 5 段:工具函数、命令入口、核心插入流程、两种配准文件解析器、Lee Mac 多文件选择对话框。下面按数据流顺序讲关键的几段。
1. 命令入口:批量循环
(defun c:insgeo ( / image-list total index path )
(setq image-list (LM:getfiles "Select Geo-Referenced Image(s)"
""
*insgeo-image-exts*))
(setq total (length image-list)
index 0)
(foreach path image-list
(setq index (1+ index))
(princ (strcat "\nInserting image " (itoa index) " of " (itoa total) " ..."))
(insgeo:place-image path)
)
(if (> total 0)
(princ (strcat "\nDone. " (itoa total) " image(s) inserted."))
)
(princ)
)
入口很薄:调用 LM:getfiles 拿到一个文件路径列表,然后 foreach 逐个交给 insgeo:place-image 处理。这个写法天然兼顾"单张"和"批量"——选 1 个文件时列表只有 1 个元素,循环一次就结束,所以不需要专门的单张命令。
2. 核心插入流程:insgeo:place-image
(defun insgeo:place-image ( image-path / image base sidecar ext minpt maxpt )
;; 把影像先插到原点,稍后再根据配准文件挪到正确位置
(setq image (insgeo:add-raster image-path))
;; 去掉影像扩展名,得到查找配准文件的"基名"
(setq base (vl-string-subst
""
(vl-filename-extension (vla-get-imagefile image))
(vla-get-imagefile image)))
;; 按优先级探测配套配准文件,都找不到则弹框让用户选
(setq sidecar
(cond
((findfile (strcat base ".ers")))
((findfile (strcat base ".tfw")))
((findfile (strcat base ".jgw")))
((findfile (strcat base ".pgw")))
(t (getfiled "Select World File"
base
"tfw;jgw;pgw;ers"
0))
)
)
(if sidecar
(progn
(setq ext (strcase (vl-filename-extension sidecar)))
(cond
((= ext ".ERS") (insgeo:apply-ers image sidecar))
(t (insgeo:apply-worldfile image sidecar))
)
;; 缩放到刚插入的影像范围
(vla-getboundingbox image 'minpt 'maxpt)
(vla-zoomwindow (vlax-get-acad-object) minpt maxpt)
)
(princ (strcat "\nNo sidecar found for "
(vl-filename-base image-path)
"; image left at origin."))
)
(princ)
)
整个函数是"插入 → 找配准 → 应用配准 → 自动缩放"四步:
- 先插入再配准——
insgeo:add-raster把影像加到当前布局的 (0,0),返回 VLA 对象,后续只需要修改它的Origin、Rotation、ImageWidth、ImageHeight四个属性 - 配准文件探测优先级 ERS → TFW → JGW → PGW——
cond利用了 LISP 的特性:每个分支只有一个表达式,如果该表达式返回非空(findfile返回路径)就当作整个cond的值。这样写比if-else嵌套清爽很多 - 格式分发——
.ERS走 ER Mapper 解析器,其它三种走标准 World File 解析器(它们的格式完全一样,只是扩展名不同) vla-zoomwindow——插入完直接缩放到该影像的包围盒,用户立刻能看到结果,不需要再 zoom extents
3. 标准 World File 解析:insgeo:apply-worldfile
ESRI World File 是固定的 6 行文本格式。脚本严格按行读取并应用变换:
(defun insgeo:apply-worldfile ( image path / fp x-size x-rot y-rot y-size origin )
(setq fp (open path "r")
x-size (atof (read-line fp)) ; 第 1 行:X 方向像素大小
x-rot (atof (read-line fp)) ; 第 2 行:X 旋转
y-rot (atof (read-line fp)) ; 第 3 行:Y 旋转
y-size (atof (read-line fp)) ; 第 4 行:Y 方向像素大小(通常为负)
origin (list (atof (read-line fp)) ; 第 5 行:左上像素中心 X
(atof (read-line fp)) ; 第 6 行:左上像素中心 Y
0.0))
(close fp)
;; World File 给的是"左上像素中心",但 AutoCAD 的影像原点是"左下角"。
;; 沿旋转后的左边沿往下走 (像素行数 × |y-size|) 就到左下角。
(setq origin (polar origin
(degrad (- x-rot 90.0))
(* (vla-get-height image) (abs y-size))))
(vla-put-rotation image (degrad x-rot))
(vla-put-origin image (insgeo:point->variant origin))
(vla-put-imageheight image (* (vla-get-height image) (abs y-size)))
(vla-put-imagewidth image (* (vla-get-width image) (abs x-size)))
)
最关键的是中间那行 polar 的用法。坑点在于坐标基准点不同:
- World File 标准里,第 5、6 行给出的是影像左上角像素的中心点世界坐标
- AutoCAD 的
Origin属性指影像左下角
所以即使读到了 X/Y,也不能直接 vla-put-origin,必须沿着旋转后的影像左边沿往下平移一整张影像的高度,才是真正的左下角。polar 接收"起点+方向角+距离",一次完成这个平移。
4. ERS 解析:按关键字搜索而不是按行号
老版本脚本对 ERS 文件用 (repeat 19 (read-line fp)) 跳过前 19 行,然后假定第 20、21 行就是坐标——只要 ERS 头多一个字段或一个波段就会读错位置。新实现按关键字逐行扫描:
(defun insgeo:apply-ers ( image path / fp line
east north
x-size y-size
reg-col reg-row
top-left-x top-left-y )
(setq fp (open path "r")
x-size 1.0 ; pixel width fallback
y-size 1.0 ; pixel height fallback
reg-col 0.0 ; registration column (0 = leftmost)
reg-row 0.0) ; registration row (0 = topmost)
;; 通读全文,按关键字摘出关心的字段
(while (setq line (read-line fp))
(cond
((insgeo:ers-key? line "Eastings") (setq east (insgeo:ers-value line)))
((insgeo:ers-key? line "Northings") (setq north (insgeo:ers-value line)))
((insgeo:ers-key? line "Xdimension") (setq x-size (insgeo:ers-value line)))
((insgeo:ers-key? line "Ydimension") (setq y-size (insgeo:ers-value line)))
((insgeo:ers-key? line "RegistrationCellX") (setq reg-col (insgeo:ers-value line)))
((insgeo:ers-key? line "RegistrationCellY") (setq reg-row (insgeo:ers-value line)))
)
)
(close fp)
(if (and east north)
(progn
;; 把"配准点"移回左上像素角...
(setq top-left-x (- east (* reg-col x-size))
top-left-y (+ north (* reg-row y-size)))
;; ...再下移一整张图高,得到 AutoCAD 要的左下角原点
(vla-put-origin image
(insgeo:point->variant
(list top-left-x
(- top-left-y (* (vla-get-height image) y-size))
0.0)))
(vla-put-imagewidth image (* (vla-get-width image) x-size))
(vla-put-imageheight image (* (vla-get-height image) y-size))
)
(princ (strcat "\nWarning: Eastings/Northings not found in " path))
)
)
两个细节值得说明:
RegistrationCellX/Y——ERS 规范允许"配准点"是影像里任意一个像素(不一定是左上角),这个字段标明它在第几列、第几行。代码先用它把配准坐标平移回 (0,0) 像素,再统一按"左上 → 左下"换算成 AutoCAD 原点。老版本完全忽略了这两个字段,假设永远是左上角,在某些 ERS 文件上会算偏- 找不到坐标时打印警告——避免静默失败。
east和north没找到会进入princ分支,告诉用户配准没生效
辅助函数 insgeo:ers-key? 和 insgeo:ers-value 配合工作:前者用 vl-string-search 做大小写不敏感的子串匹配,后者从 = 右侧切出数值并 vl-string-trim 掉空白。这样写对 ERS 文件中各种空白格式(Eastings = 12345.0、Eastings=12345.0、带制表符等)都能正确解析。
5. 多文件对话框
LM:getfiles 是 Lee Mac 写的多文件选择库(v1.6,2016 年版),原生 AutoCAD 的 getfiled 只能选一个文件,这个库填补了这个空缺。它运行时动态生成 DCL 临时文件来定义对话框界面,使用完自动删除——INSGEO.lsp 把它直接内嵌进来,无需另外加载。
文档前面那两张截图中的"两栏布局 + 文件夹路径栏 + Browse 按钮 + 列标题"都对应 DCL 里的几行声明(在脚本 5. LM:getfiles 一节里),如果想调整列表框大小、按钮宽度、列标题文字,只需要改那段 DCL 字符串即可。
针对 UNC 路径的小修改:LM:getfiles v1.6 原版用 vl-directory-files 列目录,这个 AutoLISP 内置函数对 NetBIOS 计算机名形式的 UNC 路径支持不完整——\\HOSTNAME\share 会返回空列表,必须改用 \\IP\share 才能工作。脚本里把这部分换成了 Scripting.FileSystemObject(FSO),增加了一个 LM:getfiles:fso 函数作为替代,带回退路径(FSO 失败则退回原函数,行为不会比原版更差)。修复后计算机名 UNC 完全可用,团队协作时可以直接用 \\HOSTNAME\share,省去对静态 IP 的依赖:
;;; ADDED 2026 by isweibin (not part of original Lee Mac code):
;;; List directory contents via Scripting.FileSystemObject. `kind' is
;;; 'subfolders or 'files. Falls back to vl-directory-files if FSO fails
;;; (so behaviour never gets worse than the original).
(defun LM:getfiles:fso ( dir kind / fso folder result )
(setq fso (vlax-create-object "Scripting.FileSystemObject"))
(setq result
(vl-catch-all-apply
(function
(lambda ()
(setq folder (vlax-invoke fso 'GetFolder dir)
result (if (eq kind 'subfolders) '(".." ".") nil))
(vlax-for item (vlax-invoke folder
(if (eq kind 'subfolders) 'SubFolders 'Files))
(setq result (cons (vla-get-name item) result)))
(reverse result)))))
(vlax-release-object fso)
(if (vl-catch-all-error-p result)
(vl-directory-files dir nil (if (eq kind 'subfolders) -1 1))
result)
)
这处修改在源码里对应 SPDX-Snippet 块的注释中明确披露。Lee Mac 原版的版权声明、freeware 非商用条款都完整保留;基于这部分代码做的修改,版权归 isweibin,但许可证仍然继承 Lee Mac 原条款(整个文件因此不能商用,除非未来彻底替换 LM:getfiles)。
项目结构速查
| 区段 | 内容 | 主要函数 |
|---|---|---|
| 1 | 角度工具 | degrad / raddeg |
| 2 | 命令入口 | c:insgeo |
| 3 | 核心插入流程 | insgeo:place-image / insgeo:add-raster |
| 4a | World File 解析 | insgeo:apply-worldfile |
| 4b | ERS 解析 | insgeo:apply-ers / insgeo:ers-key? / insgeo:ers-value |
| 4c | COM 辅助 | insgeo:point->variant |
| 5 | 多文件对话框 | LM:getfiles 及一系列 LM:getfiles:* 辅助(含 LM:getfiles:fso) |
致谢
- 决策英明果断 —— 作者大人:提供初版脚本文件、把命令名定为更贴合的
INSGEO、砍掉冗余的单张命令、点出 ERS 解析的硬编码隐患、要求注释遵循 UMBC Lisp 风格、发现 NetBIOS UNC 兼容性问题并推动 FSO 修复——每一步设计走向都来自这些指挥若定的判断 😊 - 编码劳苦功高 —— Claude:负责把上述决断翻译成 LISP、修 ERS bug、分层注释、画 DCL、用 FSO 修补 UNC 支持、写本文档,字字句句任劳任怨 😤
- 多文件对话框 —— Lee Mac 的 LM:getfiles v1.6(2016-03-21):一段成熟的开源代码,直接套用让批量选择体验立刻到位,省去了从零手搓对话框的好几小时 🙏
本文采用 CC BY-NC-SA 4.0 协议发布,可自由转载、修改,但需保留作者署名、不可用于商业用途、衍生作品需以相同协议发布。