DigitalWatermarkerTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. using System;
  2. using System.IO;
  3. using Masuit.Tools.DigtalWatermarker;
  4. using OpenCvSharp;
  5. using Xunit;
  6. namespace Masuit.Tools.DigitalWatermarker.Test;
  7. public class DigitalWatermarkerTests : IDisposable
  8. {
  9. private readonly string _testImageDirectory;
  10. private readonly string _sourceImagePath;
  11. private readonly string _watermarkImagePath;
  12. private readonly Mat _sourceImage;
  13. private readonly Mat _watermarkImage;
  14. public DigitalWatermarkerTests()
  15. {
  16. // 创建测试目录
  17. _testImageDirectory = Path.Combine(Path.GetTempPath(), "DigitalWatermarkerTests", Guid.NewGuid().ToString());
  18. Directory.CreateDirectory(_testImageDirectory);
  19. // 创建测试图像文件路径
  20. _sourceImagePath = Path.Combine(_testImageDirectory, "source.jpg");
  21. _watermarkImagePath = Path.Combine(_testImageDirectory, "watermark.png");
  22. // 创建测试用的源图像 (512x512 彩色图像)
  23. _sourceImage = CreateTestSourceImage();
  24. // 创建测试用的水印图像 (64x64 二值图像)
  25. _watermarkImage = CreateTestWatermarkImage();
  26. // 保存测试图像到文件
  27. Cv2.ImWrite(_sourceImagePath, _sourceImage);
  28. Cv2.ImWrite(_watermarkImagePath, _watermarkImage);
  29. }
  30. public void Dispose()
  31. {
  32. _sourceImage?.Dispose();
  33. _watermarkImage?.Dispose();
  34. // 清理测试文件
  35. if (Directory.Exists(_testImageDirectory))
  36. {
  37. Directory.Delete(_testImageDirectory, true);
  38. }
  39. }
  40. #region 测试Mat对象方法
  41. [Fact]
  42. public void EmbedWatermark_WithValidMatObjects_ShouldReturnWatermarkedImage()
  43. {
  44. // Act
  45. using var result = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImage, _watermarkImage);
  46. // Assert
  47. Assert.NotNull(result);
  48. Assert.False(result.Empty());
  49. Assert.Equal(_sourceImage.Size(), result.Size());
  50. Assert.Equal(_sourceImage.Type(), result.Type());
  51. }
  52. [Fact]
  53. public void EmbedWatermark_WithEmptySource_ShouldThrowArgumentException()
  54. {
  55. // Arrange
  56. using var emptyMat = new Mat();
  57. // Act & Assert
  58. var exception = Assert.Throws<ArgumentException>(() =>
  59. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(emptyMat, _watermarkImage));
  60. Assert.Contains("source is empty", exception.Message);
  61. }
  62. [Fact]
  63. public void EmbedWatermark_WithEmptyWatermark_ShouldThrowArgumentException()
  64. {
  65. // Arrange
  66. using var emptyMat = new Mat();
  67. // Act & Assert
  68. var exception = Assert.Throws<ArgumentException>(() =>
  69. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImage, emptyMat));
  70. Assert.Contains("watermark is empty", exception.Message);
  71. }
  72. [Fact]
  73. public void ExtractWatermark_WithValidImage_ShouldReturnWatermark()
  74. {
  75. // Arrange
  76. using var watermarkedImage = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImage, _watermarkImage);
  77. // Act
  78. using var extractedWatermark = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(watermarkedImage);
  79. // Assert
  80. Assert.NotNull(extractedWatermark);
  81. Assert.False(extractedWatermark.Empty());
  82. Assert.Equal(MatType.CV_8U, extractedWatermark.Type());
  83. }
  84. [Fact]
  85. public void ExtractWatermark_WithEmptyImage_ShouldThrowArgumentException()
  86. {
  87. // Arrange
  88. using var emptyMat = new Mat();
  89. // Act & Assert
  90. var exception = Assert.Throws<ArgumentException>(() =>
  91. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(emptyMat));
  92. Assert.Contains("image is empty", exception.Message);
  93. }
  94. #endregion
  95. #region 测试文件路径方法
  96. [Fact]
  97. public void EmbedWatermark_WithValidFilePaths_ShouldReturnWatermarkedImage()
  98. {
  99. // Act
  100. using var result = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImagePath, _watermarkImagePath);
  101. // Assert
  102. Assert.NotNull(result);
  103. Assert.False(result.Empty());
  104. Assert.Equal(_sourceImage.Size(), result.Size());
  105. Assert.Equal(_sourceImage.Type(), result.Type());
  106. }
  107. [Fact]
  108. public void EmbedWatermark_WithNullSourcePath_ShouldThrowArgumentException()
  109. {
  110. // Act & Assert
  111. var exception = Assert.Throws<ArgumentException>(() =>
  112. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(null, _watermarkImagePath));
  113. Assert.Contains("图片路径不能为空", exception.Message);
  114. }
  115. [Fact]
  116. public void EmbedWatermark_WithEmptySourcePath_ShouldThrowArgumentException()
  117. {
  118. // Act & Assert
  119. var exception = Assert.Throws<ArgumentException>(() =>
  120. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark("", _watermarkImagePath));
  121. Assert.Contains("图片路径不能为空", exception.Message);
  122. }
  123. [Fact]
  124. public void EmbedWatermark_WithNullWatermarkPath_ShouldThrowArgumentException()
  125. {
  126. // Act & Assert
  127. var exception = Assert.Throws<ArgumentException>(() =>
  128. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImagePath, null));
  129. Assert.Contains("水印图片路径不能为空", exception.Message);
  130. }
  131. [Fact]
  132. public void EmbedWatermark_WithNonExistentSourceFile_ShouldThrowFileNotFoundException()
  133. {
  134. // Act & Assert
  135. var exception = Assert.Throws<FileNotFoundException>(() =>
  136. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark("nonexistent.jpg", _watermarkImagePath));
  137. Assert.Contains("文件不存在", exception.Message);
  138. }
  139. [Fact]
  140. public void EmbedWatermark_WithNonExistentWatermarkFile_ShouldThrowFileNotFoundException()
  141. {
  142. // Act & Assert
  143. var exception = Assert.Throws<FileNotFoundException>(() =>
  144. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImagePath, "nonexistent.png"));
  145. Assert.Contains("文件不存在", exception.Message);
  146. }
  147. [Fact]
  148. public void ExtractWatermark_WithValidFilePath_ShouldReturnWatermark()
  149. {
  150. // Arrange
  151. using var watermarkedImage = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImagePath, _watermarkImagePath);
  152. var watermarkedImagePath = Path.Combine(_testImageDirectory, "watermarked.jpg");
  153. Cv2.ImWrite(watermarkedImagePath, watermarkedImage);
  154. // Act
  155. using var extractedWatermark = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(watermarkedImagePath);
  156. // Assert
  157. Assert.NotNull(extractedWatermark);
  158. Assert.False(extractedWatermark.Empty());
  159. }
  160. [Fact]
  161. public void ExtractWatermark_WithNullPath_ShouldThrowArgumentException()
  162. {
  163. // Act & Assert
  164. var exception = Assert.Throws<ArgumentException>(() =>
  165. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark((string)null!));
  166. Assert.Contains("路径不能为空", exception.Message);
  167. }
  168. [Fact]
  169. public void ExtractWatermark_WithNonExistentFile_ShouldThrowFileNotFoundException()
  170. {
  171. // Act & Assert
  172. var exception = Assert.Throws<FileNotFoundException>(() =>
  173. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark("nonexistent.jpg"));
  174. Assert.Contains("文件不存在", exception.Message);
  175. }
  176. #endregion
  177. #region 测试Stream方法
  178. [Fact]
  179. public void EmbedWatermark_WithValidStreams_ShouldReturnWatermarkedImage()
  180. {
  181. // Arrange
  182. using var sourceStream = new MemoryStream();
  183. using var watermarkStream = new MemoryStream();
  184. Cv2.ImEncode(".jpg", _sourceImage, out var sourceBytes);
  185. Cv2.ImEncode(".png", _watermarkImage, out var watermarkBytes);
  186. sourceStream.Write(sourceBytes);
  187. watermarkStream.Write(watermarkBytes);
  188. sourceStream.Position = 0;
  189. watermarkStream.Position = 0;
  190. // Act
  191. using var result = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(sourceStream, watermarkStream);
  192. // Assert
  193. Assert.NotNull(result);
  194. Assert.False(result.Empty());
  195. Assert.Equal(_sourceImage.Size(), result.Size());
  196. Assert.Equal(_sourceImage.Type(), result.Type());
  197. }
  198. [Fact]
  199. public void EmbedWatermark_WithNullSourceStream_ShouldThrowArgumentNullException()
  200. {
  201. // Arrange
  202. using var watermarkStream = new MemoryStream();
  203. // Act & Assert
  204. Assert.Throws<ArgumentNullException>(() =>
  205. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(null, watermarkStream));
  206. }
  207. [Fact]
  208. public void EmbedWatermark_WithNullWatermarkStream_ShouldThrowArgumentNullException()
  209. {
  210. // Arrange
  211. using var sourceStream = new MemoryStream();
  212. // Act & Assert
  213. Assert.Throws<ArgumentNullException>(() =>
  214. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(sourceStream, null));
  215. }
  216. [Fact]
  217. public void ExtractWatermark_WithValidStream_ShouldReturnWatermark()
  218. {
  219. // Arrange
  220. using var watermarkedImage = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImage, _watermarkImage);
  221. using var imageStream = new MemoryStream();
  222. Cv2.ImEncode(".jpg", watermarkedImage, out var imageBytes);
  223. imageStream.Write(imageBytes);
  224. imageStream.Position = 0;
  225. // Act
  226. using var extractedWatermark = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(imageStream);
  227. // Assert
  228. Assert.NotNull(extractedWatermark);
  229. Assert.False(extractedWatermark.Empty());
  230. }
  231. [Fact]
  232. public void ExtractWatermark_WithNullStream_ShouldThrowArgumentNullException()
  233. {
  234. // Act & Assert
  235. Assert.Throws<ArgumentNullException>(() =>
  236. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark((Stream)null!));
  237. }
  238. [Fact]
  239. public void ExtractWatermark_WithInvalidImageStream_ShouldThrowArgumentException()
  240. {
  241. // Arrange
  242. using var invalidStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
  243. // Act & Assert
  244. var exception = Assert.Throws<ArgumentException>(() =>
  245. Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(invalidStream));
  246. Assert.Contains("stream不能解析为图像", exception.Message);
  247. }
  248. #endregion
  249. #region 集成测试
  250. [Fact]
  251. public void WatermarkRoundTrip_ShouldPreserveWatermarkPattern()
  252. {
  253. // Arrange - 创建一个有明确模式的水印
  254. using var patternWatermark = CreatePatternWatermark();
  255. // Act - 嵌入并提取水印
  256. using var watermarkedImage = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImage, patternWatermark);
  257. using var extractedWatermark = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(watermarkedImage);
  258. // Assert - 验证提取的水印不为空且有合理的尺寸
  259. Assert.NotNull(extractedWatermark);
  260. Assert.False(extractedWatermark.Empty());
  261. Assert.True(extractedWatermark.Rows > 0);
  262. Assert.True(extractedWatermark.Cols > 0);
  263. }
  264. [Fact]
  265. public void WatermarkShouldBeRobustToJpegCompression()
  266. {
  267. // Arrange
  268. using var watermarkedImage = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.EmbedWatermark(_sourceImage, _watermarkImage);
  269. // 模拟JPEG压缩
  270. var compressionParams = new int[] { (int)ImwriteFlags.JpegQuality, 75 };
  271. Cv2.ImEncode(".jpg", watermarkedImage, out var compressedBytes, compressionParams);
  272. using var compressedImage = Cv2.ImDecode(compressedBytes, ImreadModes.Color);
  273. // Act
  274. using var extractedWatermark = Masuit.Tools.DigtalWatermarker.DigitalWatermarker.ExtractWatermark(compressedImage);
  275. // Assert
  276. Assert.NotNull(extractedWatermark);
  277. Assert.False(extractedWatermark.Empty());
  278. }
  279. #endregion
  280. #region 辅助方法
  281. private static Mat CreateTestSourceImage()
  282. {
  283. var image = new Mat(512, 512, MatType.CV_8UC3);
  284. // 创建渐变背景
  285. for (int y = 0; y < image.Rows; y++)
  286. {
  287. for (int x = 0; x < image.Cols; x++)
  288. {
  289. byte intensity = (byte)(128 + (x + y) % 128);
  290. image.Set(y, x, new Vec3b(intensity, (byte)(255 - intensity), (byte)(intensity / 2)));
  291. }
  292. }
  293. return image;
  294. }
  295. private static Mat CreateTestWatermarkImage()
  296. {
  297. var watermark = new Mat(64, 64, MatType.CV_8UC1, Scalar.Black);
  298. // 创建简单的棋盘格模式
  299. for (int y = 0; y < watermark.Rows; y++)
  300. {
  301. for (int x = 0; x < watermark.Cols; x++)
  302. {
  303. if ((x / 8 + y / 8) % 2 == 0)
  304. {
  305. watermark.Set(y, x, (byte)255);
  306. }
  307. }
  308. }
  309. return watermark;
  310. }
  311. private static Mat CreatePatternWatermark()
  312. {
  313. var watermark = new Mat(32, 32, MatType.CV_8UC1, Scalar.Black);
  314. // 创建十字形模式
  315. int center = 16;
  316. for (int i = 0; i < 32; i++)
  317. {
  318. watermark.Set(center, i, (byte)255); // 水平线
  319. watermark.Set(i, center, (byte)255); // 垂直线
  320. }
  321. return watermark;
  322. }
  323. #endregion
  324. }