DigitalWatermarkerTests.cs 13 KB

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