C#实现高性能拍照与水印添加功能完整方案

2025-09-24 12:50

本文主要是介绍C#实现高性能拍照与水印添加功能完整方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C#实现高性能拍照与水印添加功能完整方案》在工业检测、质量追溯等应用场景中,经常需要对产品进行拍照并添加相关信息水印,本文将详细介绍如何使用C#实现一个高性能的拍照和水印添加功能,包含完整的代码实现...

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 SolijsdColorBrush(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(OldCodChina编程e) ? "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. 智python能水印实现

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 = 
    www.chinasem.cn{
        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. 性能优化技巧

  1. 资源管理:使用using语句和SafeHBitmapHandle确保资源正确释放
  2. 异常处理:完善的异常处理机制保证程序稳定性
  3. 配置化:通过配置文件管理路径、旋转设置等参数
  4. 高质量渲染:设置合适的图形渲染参数保证图像质量
  5. 日志记录:详细的日志记录便于问题排查

7. 实际应用建议

  1. 相机选型:根据实际需求选择合适分辨率的工业相机
  2. 存储规划:考虑图片数量和大小,合理规划存储空间
  3. 性能监控:监控拍照过程的耗时,优化瓶颈环节
  4. 错误处理:增加重试机制应对临时性故障
  5. 内存管理:定期清理不必要的资源,防止内存泄漏

8. 总结

本文详细介绍了C#实现拍照和水印添加功能的完整方案,涵盖了从相机控制、图像处理到文件保存的各个环节。代码中体现了良好的资源管理、异常处理和性能优化实践,可以直接应用于工业检测、质量追溯等实际场景。

通过合理的架构设计和细节处理,这个方案能够稳定高效地完成拍照和水印添加任务,为后续的图像分析和数据处理提供可靠的基础。

以上就是C#实现高性能拍照与水印添加功能完整方案的详细内容,更多关于C#拍照与水印添加功能的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于C#实现高性能拍照与水印添加功能完整方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156004

相关文章

C#使用iText获取PDF的trailer数据的代码示例

《C#使用iText获取PDF的trailer数据的代码示例》开发程序debug的时候,看到了PDF有个trailer数据,挺有意思,于是考虑用代码把它读出来,那么就用到我们常用的iText框架了,所... 目录引言iText 核心概念C# 代码示例步骤 1: 确保已安装 iText步骤 2: C# 代码程

C#实现SHP文件读取与地图显示的完整教程

《C#实现SHP文件读取与地图显示的完整教程》在地理信息系统(GIS)开发中,SHP文件是一种常见的矢量数据格式,本文将详细介绍如何使用C#读取SHP文件并实现地图显示功能,包括坐标转换、图形渲染、平... 目录概述功能特点核心代码解析1. 文件读取与初始化2. 坐标转换3. 图形绘制4. 地图交互功能缩放

java读取excel文件为base64实现方式

《java读取excel文件为base64实现方式》文章介绍使用ApachePOI和EasyExcel处理Excel文件并转换为Base64的方法,强调EasyExcel适合大文件且内存占用低,需注意... 目录使用 Apache POI 读取 Excel 并转换为 Base64使用 EasyExcel 处

Python动态处理文件编码的完整指南

《Python动态处理文件编码的完整指南》在Python文件处理的高级应用中,我们经常会遇到需要动态处理文件编码的场景,本文将深入探讨Python中动态处理文件编码的技术,有需要的小伙伴可以了解下... 目录引言一、理解python的文件编码体系1.1 Python的IO层次结构1.2 编码问题的常见场景二

Python实现简单封装网络请求的示例详解

《Python实现简单封装网络请求的示例详解》这篇文章主要为大家详细介绍了Python实现简单封装网络请求的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录安装依赖核心功能说明1. 类与方法概览2.NetHelper类初始化参数3.ApiResponse类属性与方法使用实

Spring定时任务之fixedRateString的实现示例

《Spring定时任务之fixedRateString的实现示例》本文主要介绍了Spring定时任务之fixedRateString的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录从毫秒到 Duration:为何要改变?核心:Java.time.Duration.parse

Python进行word模板内容替换的实现示例

《Python进行word模板内容替换的实现示例》本文介绍了使用Python自动化处理Word模板文档的常用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录技术背景与需求场景核心工具库介绍1.获取你的word模板内容2.正常文本内容的替换3.表格内容的

docker编写java的jar完整步骤记录

《docker编写java的jar完整步骤记录》在平常的开发工作中,我们经常需要部署项目,开发测试完成后,最关键的一步就是部署,:本文主要介绍docker编写java的jar的相关资料,文中通过代... 目录all-docker/生成Docker打包部署文件配置服务A的Dockerfile (a/Docke

Java中实现对象的拷贝案例讲解

《Java中实现对象的拷贝案例讲解》Java对象拷贝分为浅拷贝(复制值及引用地址)和深拷贝(递归复制所有引用对象),常用方法包括Object.clone()、序列化及JSON转换,需处理循环引用问题,... 目录对象的拷贝简介浅拷贝和深拷贝浅拷贝深拷贝深拷贝和循环引用总结对象的拷贝简介对象的拷贝,把一个

linux部署NFS和autofs自动挂载实现过程

《linux部署NFS和autofs自动挂载实现过程》文章介绍了NFS(网络文件系统)和Autofs的原理与配置,NFS通过RPC实现跨系统文件共享,需配置/etc/exports和nfs.conf,... 目录(一)NFS1. 什么是NFS2.NFS守护进程3.RPC服务4. 原理5. 部署5.1安装NF