C#使用twaindotnet拼接扫描图像出现大面积空白问题
问题解答
1. Image.Height 与 Bitmap.Height 是否存在差异
不存在取值差异。Bitmap 类继承自 Image 类,Height 属性是 Image 基类定义的成员,返回图像的实际像素高度,二者返回值完全一致,不是产生空白问题的原因。
2. 大面积空白的根本原因
空白来自GDI+ DrawImage 方法的默认自动缩放逻辑:
- Twain 扫描输出的图像自带DPI元数据(通常为扫描仪设置的200/300/600DPI),而通过
new Bitmap()创建的拼接画布如果不手动指定DPI,会默认使用系统屏幕DPI(绝大多数设备为96DPI)。 - 调用无矩形参数的
DrawImage重载时,GDI+不会按1:1像素绘制图像,而是会根据源图和目标画布的DPI差值自动计算缩放比例。比如300DPI的扫描图绘制到96DPI的画布上,实际绘制尺寸会放大到原像素尺寸的300/96≈3.125倍,观察到的4倍左右高度差、图像下方大面积空白,就是旧图和新图都被放大绘制、超出创建的Bitmap尺寸后,画布剩余区域显示默认灰色导致的。 - 原代码额外存在隐患:每次替换
pictureBox1.Image后没有释放旧图像资源,连续扫描会出现内存泄漏。
3. 修复代码
修复核心是三点:新建画布时匹配扫描图DPI、调用DrawImage时明确指定按像素1:1绘制、及时释放旧资源。
private void _twain_TransferImage(object sender, TransferImageEventArgs e) { if (e.Image == null) return; Image newScanImg = e.Image; if (pictureBox1.Image == null) { pictureBox1.Image = newScanImg; return; } Image oldCombinedImg = pictureBox1.Image; // 新画布宽度取两张图最大值避免裁切,高度为两张图像素高度之和 int canvasWidth = Math.Max(oldCombinedImg.Width, newScanImg.Width); int canvasHeight = oldCombinedImg.Height + newScanImg.Height; Bitmap combinedBmp = new Bitmap(canvasWidth, canvasHeight); // 关键:匹配扫描图DPI,避免自动缩放 combinedBmp.SetResolution(newScanImg.HorizontalResolution, newScanImg.VerticalResolution); using (Graphics g = Graphics.FromImage(combinedBmp)) { // 画布填充白色,避免透明/灰色底 g.Clear(Color.White); // 绘制已拼接的旧图,强制按像素1:1渲染 g.DrawImage(oldCombinedImg, new Rectangle(0, 0, oldCombinedImg.Width, oldCombinedImg.Height), new Rectangle(0, 0, oldCombinedImg.Width, oldCombinedImg.Height), GraphicsUnit.Pixel); // 新扫描图绘制在旧图正下方 g.DrawImage(newScanImg, new Rectangle(0, oldCombinedImg.Height, newScanImg.Width, newScanImg.Height), new Rectangle(0, 0, newScanImg.Width, newScanImg.Height), GraphicsUnit.Pixel); } // 释放旧资源,避免内存泄漏 Image oldImg = pictureBox1.Image; pictureBox1.Image = combinedBmp; oldImg.Dispose(); newScanImg.Dispose(); }
修复后拼接的长图总高度为所有扫描图像素高度之和,不会出现多余空白,多张扫描图会紧密上下拼接。
内容的提问来源于stack exchange,提问作者GuidoG
相关产品推荐
相关产品推荐

