1. 概述
在工业检测、质量追溯等应用场景中,经常需要对产品进行拍照并添加相关信息水印。本文将详细介绍如何使用c#实现一个高性能的拍照和水印添加功能,包含完整的代码实现和优化技巧。
2. 功能架构设计
本功能主要分为以下几个模块:
- 相机控制与分辨率设置
- 图像捕获与处理
- 水印信息生成与添加
- 文件保存与ui更新
3. 核心代码实现
3.1 主拍照方法
/// <summary> /// 拍照并添加水印信息 /// </summary> /// <returns>保存的图片路径,失败返回空字符串</returns> private string takepictures() { console.writeline("开始拍照"); string filepath = string.empty; try { this.dispatcher.invoke(() => { // 1. 设置ui背景 imgbox.background = new solidcolorbrush(system.windows.media.color.fromrgb(75, 76, 69)); // 2. 设置相机分辨率 if (!setcameraresolutionwithoutstop(3840, 2160)) { console.writeline("设置分辨率失败"); return; } // 3. 检查视频帧有效性 if (player?.getcurrentvideoframe()?.gethbitmap() == intptr.zero) { console.writeline("获取视频帧失败"); return; } // 4. 获取并处理图像 using (var hbitmap = new safehbitmaphandle(player.getcurrentvideoframe().gethbitmap())) { bitmapsource bitmapsource = createbitmapsourcefromhbitmap(hbitmap); if (bitmapsource == null) { console.writeline("创建bitmapsource失败"); return; } // 5. 旋转图像(如果需要) if (shouldrotateimage()) { bitmapsource = rotateimage(bitmapsource, 180); } // 6. 保存图像 filepath = generatefilepath(); if (string.isnullorempty(filepath)) { console.writeline("生成文件路径失败"); return; } saveimagetofile(bitmapsource, filepath); // 7. 添加水印 addwatermarktoimage(filepath, currentscancoderecord); // 8. 更新ui显示 updateimagedisplay(filepath); console.writeline($"照片已保存到: {path.getfullpath(filepath)}"); systeminfo.writefile($"照片已保存到: {path.getfullpath(filepath)}", "生产日志"); } }); return filepath; } catch (exception ex) { console.writeline($"照片保存出错: {ex}"); systeminfo.writefile($"照片保存出错: {ex}", "error"); return string.empty; } }
3.2 安全hbitmap处理类
// 安全释放hbitmap的包装类 private class safehbitmaphandle : idisposable { private readonly intptr _hbitmap; private bool _disposed = false; public safehbitmaphandle(intptr hbitmap) { _hbitmap = hbitmap; } public static implicit operator intptr(safehbitmaphandle handle) => handle._hbitmap; public void dispose() { if (!_disposed && _hbitmap != intptr.zero) { deleteobject(_hbitmap); _disposed = true; } gc.suppressfinalize(this); } ~safehbitmaphandle() { dispose(); } [system.runtime.interopservices.dllimport("gdi32.dll")] private static extern bool deleteobject(intptr hobject); }
4. 关键技术点详解
4.1 相机分辨率设置
private bool setcameraresolutionwithoutstop(int width, int height) { try { if (player?.videosource is not videocapturedevice videodevice) return false; var targetresolution = videodevice.videocapabilities? .firstordefault(cap => cap.framesize.width == width && cap.framesize.height == height); if (targetresolution != null) { videodevice.videoresolution = targetresolution; console.writeline($"已设置分辨率为: {width}x{height}"); return true; } console.writeline($"未找到 {width}x{height} 分辨率支持"); return false; } catch (exception ex) { console.writeline($"设置分辨率失败: {ex.message}"); return false; } }
技术要点:
- 使用
videocapturedevice
类控制相机设备 - 通过
videocapabilities
枚举设备支持的分辨率 - 采用异常处理确保程序稳定性
4.2 图像处理与旋转
private bitmapsource rotateimage(bitmapsource source, double angle) { try { rotatetransform rotatetransform = new rotatetransform(angle); return new transformedbitmap(source, rotatetransform); } catch { return source; } }
4.3 文件路径生成策略
private string generatefilepath() { try { string basepath = configurationmanager.appsettings["picfileload"] ?? string.empty; if (string.isnullorempty(basepath)) return string.empty; string datefolder = datetime.now.tostring("yyyy-mm-dd"); string codefolder = string.isnullorempty(oldcode) ? "null" : oldcode; string directorypath = path.combine(basepath, "photos", "input", datefolder, codefolder); if (!directory.exists(directorypath)) directory.createdirectory(directorypath); return path.combine(directorypath, $"photo_{datetime.now:yyyymmddhhmmss}.jpg"); } catch { return string.empty; } }
目录结构设计:
basepath/ ├── photos/ │ ├── input/ │ │ ├── 2023-11-20/ │ │ │ ├── productcode001/ │ │ │ └── productcode002/ │ │ └── 2023-11-21/
4.4 高质量图像保存
private void saveimagetofile(bitmapsource bitmapsource, string filepath) { try { using (filestream fs = new filestream(filepath, filemode.create)) { jpegbitmapencoder encoder = new jpegbitmapencoder { qualitylevel = 95 }; encoder.frames.add(bitmapframe.create(bitmapsource)); encoder.save(fs); } } catch (exception ex) { console.writeline($"保存图像失败: {ex.message}"); throw; } }
5. 智能水印实现
5.1 水印添加核心方法
private void addwatermarktoimage(string imagepath, t_scancoderecord record) { try { using (var originalimage = image.fromfile(imagepath)) using (var watermarkimage = new bitmap(originalimage.width, originalimage.height)) using (var graphics = graphics.fromimage(watermarkimage)) { // 设置高质量绘制 graphics.smoothingmode = system.drawing.drawing2d.smoothingmode.highquality; graphics.interpolationmode = system.drawing.drawing2d.interpolationmode.highqualitybicubic; graphics.pixeloffsetmode = system.drawing.drawing2d.pixeloffsetmode.highquality; // 绘制原始图像 graphics.drawimage(originalimage, 0, 0, originalimage.width, originalimage.height); // 添加水印 string watermarktext = formatshippinginfo(record); addtextwatermark(graphics, watermarktext, originalimage.width, 80); // 保存处理后的图像 watermarkimage.save(imagepath, system.drawing.imaging.imageformat.jpeg); } } catch (exception ex) { console.writeline($"添加水印失败: {ex.message}"); throw; } }
5.2 智能文本格式化
public string formatshippinginfo(t_scancoderecord record) { if (record == null) return string.empty; string[] parts = { record.shipping_mark ?? string.empty, record.code ?? string.empty, $"{record.weight}kg", $"{record.volume}m³", datetime.now.tostring("yyyy.mm.dd hh:mm") }; // 计算显示长度(考虑中英文) int[] partlengths = parts.select(getdisplaylength).toarray(); int totallength = partlengths.sum(); // 计算间隔 int totalgaps = 126 - totallength; int gapcount = 4; int basegapsize = totalgaps / gapcount; int extraspace = totalgaps % gapcount; // 构建结果 var result = new stringbuilder(); for (int i = 0; i < parts.length; i++) { result.append(parts[i]); if (i < gapcount) { int currentgapsize = basegapsize + (extraspace > 0 ? 1 : 0); result.append(' ', currentgapsize); if (extraspace > 0) extraspace--; } } console.writeline($"最终长度: {getdisplaylength(result.tostring())}/126"); return result.tostring(); } // 计算字符串显示长度(支持中英文混合) private int getdisplaylength(string text) { if (string.isnullorempty(text)) return 0; return text.sum(c => c >= 0x4e00 && c <= 0x9fa5 ? 2 : 1); }
6. 性能优化技巧
- 资源管理:使用
using
语句和safehbitmaphandle
确保资源正确释放 - 异常处理:完善的异常处理机制保证程序稳定性
- 配置化:通过配置文件管理路径、旋转设置等参数
- 高质量渲染:设置合适的图形渲染参数保证图像质量
- 日志记录:详细的日志记录便于问题排查
7. 实际应用建议
- 相机选型:根据实际需求选择合适分辨率的工业相机
- 存储规划:考虑图片数量和大小,合理规划存储空间
- 性能监控:监控拍照过程的耗时,优化瓶颈环节
- 错误处理:增加重试机制应对临时性故障
- 内存管理:定期清理不必要的资源,防止内存泄漏
8. 总结
本文详细介绍了c#实现拍照和水印添加功能的完整方案,涵盖了从相机控制、图像处理到文件保存的各个环节。代码中体现了良好的资源管理、异常处理和性能优化实践,可以直接应用于工业检测、质量追溯等实际场景。
通过合理的架构设计和细节处理,这个方案能够稳定高效地完成拍照和水印添加任务,为后续的图像分析和数据处理提供可靠的基础。
以上就是c#实现高性能拍照与水印添加功能完整方案的详细内容,更多关于c#拍照与水印添加功能的资料请关注代码网其它相关文章!
发表评论