C#如何将隐形水印嵌入到图片或音频文件中

来源:站长查询作者:缅甸程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《C#如何将隐形水印嵌入到图片或音频文件中》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何将隐形水印嵌入到图片或音频文件中》有用,将其分享出去将是对创作者最好的鼓励。

在C#开发中,给图片或音频文件嵌入隐形水印是保护数字内容版权的常用手段,这类水印不会影响文件的正常播放或查看,同时可以在需要时验证内容归属。实现隐形水印的核心思路是利用文件格式的冗余特性,在不破坏原始内容的前提下写入自定义标识信息。

C#如何将隐形水印嵌入到图片或音频文件中

图片隐形水印嵌入实现

图片的隐形水印通常可以嵌入到像素的最低有效位(LSB)中,因为修改最低位对图片视觉效果的影响几乎可以忽略。下面以BMP格式图片为例,介绍C#实现LSB水印嵌入的方法。

水印嵌入核心逻辑

首先将水印文本转换为二进制流,然后遍历图片的像素点,把二进制位依次写入每个像素RGB通道的最低位,最后保存修改后的图片。

using System;
using System.Drawing;
using System.Text;

public class ImageWatermarkHelper
{
    // 嵌入水印到图片
    public static void EmbedWatermark(string sourceImagePath, string outputImagePath, string watermarkText)
    {
        // 加载原始图片
        using (Bitmap sourceBitmap = new Bitmap(sourceImagePath))
        {
            // 将水印文本转换为二进制数组
            byte[] watermarkBytes = Encoding.UTF8.GetBytes(watermarkText);
            string watermarkBinary = string.Join("", Array.ConvertAll(watermarkBytes, b => Convert.ToString(b, 2).PadLeft(8, '0')));
            int watermarkIndex = 0;
            int watermarkLength = watermarkBinary.Length;

            // 遍历像素写入水印
            for (int y = 0; y < sourceBitmap.Height; y++)
            {
                for (int x = 0; x < sourceBitmap.Width; x++)
                {
                    if (watermarkIndex >= watermarkLength)
                        break;

                    Color pixelColor = sourceBitmap.GetPixel(x, y);
                    // 修改R通道最低位
                    int r = pixelColor.R;
                    int newR = (r & 0xFE) | (watermarkBinary[watermarkIndex] == '1' ? 1 : 0);
                    watermarkIndex++;

                    // 修改G通道最低位
                    int g = pixelColor.G;
                    int newG = (g & 0xFE) | (watermarkBinary[watermarkIndex] == '1' ? 1 : 0);
                    watermarkIndex++;

                    // 修改B通道最低位
                    int b = pixelColor.B;
                    int newB = (b & 0xFE) | (watermarkBinary[watermarkIndex] == '1' ? 1 : 0);
                    watermarkIndex++;

                    sourceBitmap.SetPixel(x, y, Color.FromArgb(newR, newG, newB));
                }
                if (watermarkIndex >= watermarkLength)
                    break;
            }

            // 保存带水印的图片
            sourceBitmap.Save(outputImagePath);
        }
    }

    // 从图片读取水印
    public static string ReadWatermark(string watermarkImagePath, int watermarkLength)
    {
        using (Bitmap watermarkBitmap = new Bitmap(watermarkImagePath))
        {
            StringBuilder binaryBuilder = new StringBuilder();
            int readIndex = 0;
            int totalBits = watermarkLength * 8;

            // 读取像素最低位还原二进制
            for (int y = 0; y < watermarkBitmap.Height; y++)
            {
                for (int x = 0; x < watermarkBitmap.Width; x++)
                {
                    if (readIndex >= totalBits)
                        break;

                    Color pixelColor = watermarkBitmap.GetPixel(x, y);
                    binaryBuilder.Append((pixelColor.R & 1) == 1 ? '1' : '0');
                    readIndex++;
                    binaryBuilder.Append((pixelColor.G & 1) == 1 ? '1' : '0');
                    readIndex++;
                    binaryBuilder.Append((pixelColor.B & 1) == 1 ? '1' : '0');
                    readIndex++;
                }
                if (readIndex >= totalBits)
                    break;
            }

            // 二进制转字节再转文本
            byte[] watermarkBytes = new byte[watermarkLength];
            for (int i = 0; i < watermarkLength; i++)
            {
                string byteBinary = binaryBuilder.ToString(i * 8, 8);
                watermarkBytes[i] = Convert.ToByte(byteBinary, 2);
            }
            return Encoding.UTF8.GetString(watermarkBytes);
        }
    }
}

使用示例

// 嵌入水印
ImageWatermarkHelper.EmbedWatermark("original.bmp", "watermarked.bmp", "版权归属测试公司");
// 读取水印,水印长度对应文本字节数
string watermark = ImageWatermarkHelper.ReadWatermark("watermarked.bmp", 18);
Console.WriteLine(watermark);

音频隐形水印嵌入实现

音频文件的隐形水印可以嵌入到音频采样数据的最低有效位中,或者在音频的冗余频段添加标识信息,这里以WAV格式音频为例,采用LSB方式嵌入水印。

水印嵌入核心逻辑

WAV音频文件由文件头和音频数据部分组成,音频数据部分是PCM采样值,我们可以修改采样值的最低位来写入水印二进制数据。

using System;
using System.IO;
using System.Text;

public class AudioWatermarkHelper
{
    // 嵌入水印到WAV音频
    public static void EmbedWatermark(string sourceAudioPath, string outputAudioPath, string watermarkText)
    {
        byte[] audioData = File.ReadAllBytes(sourceAudioPath);
        // WAV文件头长度固定44字节
        int dataStartIndex = 44;
        // 水印转二进制
        byte[] watermarkBytes = Encoding.UTF8.GetBytes(watermarkText);
        string watermarkBinary = string.Join("", Array.ConvertAll(watermarkBytes, b => Convert.ToString(b, 2).PadLeft(8, '0')));
        int watermarkIndex = 0;

        // 遍历音频数据写入水印
        for (int i = dataStartIndex; i < audioData.Length; i++)
        {
            if (watermarkIndex >= watermarkBinary.Length)
                break;
            // 修改当前字节最低位
            audioData[i] = (byte)((audioData[i] & 0xFE) | (watermarkBinary[watermarkIndex] == '1' ? 1 : 0));
            watermarkIndex++;
        }

        File.WriteAllBytes(outputAudioPath, audioData);
    }

    // 从WAV音频读取水印
    public static string ReadWatermark(string watermarkAudioPath, int watermarkLength)
    {
        byte[] audioData = File.ReadAllBytes(watermarkAudioPath);
        int dataStartIndex = 44;
        StringBuilder binaryBuilder = new StringBuilder();
        int totalBits = watermarkLength * 8;
        int readIndex = 0;

        for (int i = dataStartIndex; i < audioData.Length; i++)
        {
            if (readIndex >= totalBits)
                break;
            binaryBuilder.Append((audioData[i] & 1) == 1 ? '1' : '0');
            readIndex++;
        }

        byte[] watermarkBytes = new byte[watermarkLength];
        for (int i = 0; i < watermarkLength; i++)
        {
            string byteBinary = binaryBuilder.ToString(i * 8, 8);
            watermarkBytes[i] = Convert.ToByte(byteBinary, 2);
        }
        return Encoding.UTF8.GetString(watermarkBytes);
    }
}

使用示例

// 嵌入水印
AudioWatermarkHelper.EmbedWatermark("original.wav", "watermarked.wav", "音频版权标识");
// 读取水印
string audioWatermark = AudioWatermarkHelper.ReadWatermark("watermarked.wav", 12);
Console.WriteLine(audioWatermark);

注意事项

  • LSB水印的抗干扰能力较弱,如果文件经过压缩、裁剪、格式转换,水印可能会丢失,实际生产中可以结合加密算法提升水印稳定性。
  • 嵌入水印前需要确认原始文件的格式是否支持直接修改数据,比如JPG是有损压缩格式,修改后重新保存可能会导致水印失效,优先选择BMP、WAV这类无损格式。
  • 水印内容不要过长,避免修改过多数据导致原始文件质量明显下降。
  • 读取水印时需要知道水印的长度,实际场景中可以在水印开头添加固定长度的标识位来记录水印长度,方便自动读取。

C#隐形水印图片水印音频水印文件内容水印修改时间:2026-07-03 04:45:32

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。