手把手教你如何给图像加水印

开发 前端
在实际的系统开发中,某些业务场景下,我们经常需要给原始图片添加水印,以防止图片信息在互联网上随意传播!

[[434006]]

本文转载自微信公众号「Java极客技术」,作者鸭血粉丝Tang。转载本文请联系Java极客技术公众号。

一、介绍

在实际的系统开发中,某些业务场景下,我们经常需要给原始图片添加水印,以防止图片信息在互联网上随意传播!

也有的基于当下的业务需求,需要给相机照片加水印、地理位置、时间等信息,以方便记录自己的生活!

例如下图!

有的人可能很容易想到,通过 PS 技术就可以很轻松的完成!

的确,对于单个图像而言很容易,但是对于成千上万的图像,采用人工处理,显然不可取!

问题来了,面对大批量的图像加水印需求,我们应当如何处理呢?

试想一下,如果我们采用人工方式来给图像添加水印,大概的步骤离不开以下几步:

  • 1、先获取需要处理的图像
  • 2、然后将图像摆放整齐,用尺子计算出我们需要加水印的位置
  • 3、采用画笔准确无误的在对应的位置上画上水印
  • 4、最后,水印添加之后!

如果采用程序来实现,思路也是一样的,废话也不多说了,代码直接撸上!

二、程序实践

下面我们以java程序为例,给以下图添加一段复印无效的文字水印,并居中!

程序实践如下:

  1. import org.apache.commons.lang3.StringUtils; 
  2. import javax.imageio.ImageIO; 
  3. import java.awt.*; 
  4. import java.awt.image.BufferedImage; 
  5. import java.io.File; 
  6.  
  7. /** 
  8.  * 给图像添加水印 
  9.  * @author pzblog 
  10.  * @since 2021-10-29 
  11.  */ 
  12. public class ImageWaterMarkUtil { 
  13.  
  14.  
  15.  
  16.     /** 
  17.      * 给图像添加文字水印 
  18.      * @param srcImgPath     原始文件地址 
  19.      * @param targetImgPath  目标文件地址 
  20.      * @param text           水印内容 
  21.      * @param color          水印文字颜色 
  22.      * @param font           水印文字字体 
  23.      * @param alpha          水印透明度 
  24.      * @param positionWidth  水印横向位置 
  25.      * @param positionHeight 水印纵向位置 
  26.      * @param degree         水印图片旋转角度 
  27.      * @param location       水印的位置,左上角、右上角、左下角、右下角、居中 
  28.      */ 
  29.     public static void markImage(String srcImgPath, 
  30.                                  String targetImgPath, 
  31.                                  String text, 
  32.                                  Color color, 
  33.                                  Font font, 
  34.                                  float alpha, 
  35.                                  int positionWidth, 
  36.                                  int positionHeight, 
  37.                                  Integer degree, 
  38.                                  String location) { 
  39.         try { 
  40.             // 1、读取源图片 
  41.             Image srcImg = ImageIO.read(new File(srcImgPath)); 
  42.             int srcImgWidth = srcImg.getWidth(null); 
  43.             int srcImgHeight = srcImg.getHeight(null); 
  44.             BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB); 
  45.  
  46.             // 2、得到画笔对象 
  47.             Graphics2D g = buffImg.createGraphics(); 
  48.             // 3、设置对线段的锯齿状边缘处理 
  49.             g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
  50.             g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0, 0, null); 
  51.             // 4、设置水印旋转 
  52.             if (null != degree) { 
  53.                 g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2); 
  54.             } 
  55.             // 5、设置水印文字颜色 
  56.             g.setColor(color); 
  57.             // 6、设置水印文字Font 
  58.             g.setFont(font); 
  59.             // 7、设置水印文字透明度 
  60.             g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); 
  61.             // 8、水印图片的位置 
  62.             int x = 0, y = 0; 
  63.             if (StringUtils.equals(location, "left-top")) { 
  64.                 x = 30; 
  65.                 y = font.getSize(); 
  66.             } else if (StringUtils.equals(location, "right-top")) { 
  67.                 x = srcImgWidth - getWatermarkLength(text, g) - 30; 
  68.                 y = font.getSize(); 
  69.             } else if (StringUtils.equals(location, "left-bottom")) { 
  70.                 x += 30; 
  71.                 y = buffImg.getHeight() - font.getSize(); 
  72.             } else if (StringUtils.equals(location, "right-bottom")) { 
  73.                 x = srcImgWidth - getWatermarkLength(text, g) - 30; 
  74.                 y = srcImgHeight - font.getSize(); 
  75.             } else if (StringUtils.equals(location, "center")) { 
  76.                 x = (srcImgWidth - getWatermarkLength(text, g)) / 2; 
  77.                 y = srcImgHeight / 2; 
  78.             } else { 
  79.                 //自定义位置 
  80.                 x = positionWidth; 
  81.                 y = positionHeight; 
  82.             } 
  83.             // 9、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y) 
  84.             g.drawString(text, x, y); 
  85.             // 10、释放资源 
  86.             g.dispose(); 
  87.             // 11、生成图片 
  88.             ImageIO.write(buffImg, "png", new File(targetImgPath)); 
  89.             System.out.println("图片完成添加水印文字"); 
  90.         } catch (Exception e) { 
  91.             e.printStackTrace(); 
  92.         } 
  93.     } 
  94.  
  95.     /** 
  96.      * 计算填充的水印长度 
  97.      * @param text 
  98.      * @param g 
  99.      * @return 
  100.      */ 
  101.     private static int getWatermarkLength(String text, Graphics2D g) { 
  102.         return g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(), 0, text.length()); 
  103.     } 
  104.  
  105.     public static void main(String[] args) { 
  106.         String srcImgPath = "/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址 
  107.         String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy.jpg"; //目标文件地址 
  108.         String text = "复 印 无 效"; //水印文字内容 
  109.         Color color = Color.red; //水印文字颜色 
  110.         Font font = new Font("宋体", Font.BOLD, 60); //水印文字字体 
  111.         float alpha = 0.4f; //水印透明度 
  112.         int positionWidth = 320; //水印横向位置坐标 
  113.         int positionHeight = 450; //水印纵向位置坐标 
  114.         Integer degree = -30; //水印旋转角度 
  115.         String location = "center"; //水印的位置 
  116.         //给图片添加文字水印 
  117.         markImage(srcImgPath, targetImgPath, text, color, font, alpha, positionWidth, positionHeight, degree, location); 
  118.     } 

运行结果如下:

水印添加成功!

2.1、给图像添加多处文字

有的需求会要求给图像添加多处文字水印,例如下图!

处理过程也很简单!

  1. import javax.imageio.ImageIO; 
  2. import java.awt.*; 
  3. import java.awt.image.BufferedImage; 
  4. import java.io.File; 
  5.  
  6. /** 
  7.  * 给图像添加水印 
  8.  * @author pzblog 
  9.  * @since 2021-10-29 
  10.  */ 
  11. public class ImageFullWaterMarkUtil { 
  12.  
  13.  
  14.  
  15.     /** 
  16.      * 给图像添加多处文字水印 
  17.      * @param srcImgPath     原始文件地址 
  18.      * @param targetImgPath  目标文件地址 
  19.      * @param text           水印内容 
  20.      * @param color          水印文字颜色 
  21.      * @param font           水印文字字体 
  22.      * @param alpha          水印透明度 
  23.      * @param startWidth     水印横向起始位置 
  24.      * @param degree         水印图片旋转角度 
  25.      * @param interval       高度间隔 
  26.      */ 
  27.     public static void fullMarkImage(String srcImgPath, 
  28.                                  String targetImgPath, 
  29.                                  String text, 
  30.                                  Color color, 
  31.                                  Font font, 
  32.                                  float alpha, 
  33.                                  int startWidth, 
  34.                                  Integer degree, 
  35.                                  Integer interval) { 
  36.         try { 
  37.             // 1、读取源图片 
  38.             Image srcImg = ImageIO.read(new File(srcImgPath)); 
  39.             int srcImgWidth = srcImg.getWidth(null); 
  40.             int srcImgHeight = srcImg.getHeight(null); 
  41.             BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB); 
  42.  
  43.             // 2、得到画笔对象 
  44.             Graphics2D g = buffImg.createGraphics(); 
  45.             // 3、设置对线段的锯齿状边缘处理 
  46.             g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
  47.             g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0, 0, null); 
  48.             // 4、设置水印旋转 
  49.             if (null != degree) { 
  50.                 g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2); 
  51.             } 
  52.             // 5、设置水印文字颜色 
  53.             g.setColor(color); 
  54.             // 6、设置水印文字Font 
  55.             g.setFont(font); 
  56.             // 7、设置水印文字透明度 
  57.             g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); 
  58.             // 8、水印图片的位置 
  59.             int x = startWidth; 
  60.             int y = font.getSize(); 
  61.             int space = srcImgHeight / interval; 
  62.             for (int i = 0; i < space; i++) { 
  63.                 //如果最后一个坐标的y轴比height高,直接退出 
  64.                 if (((y + font.getSize()) > srcImgHeight) || ((x + getWatermarkLength(text,g))  > srcImgWidth)) { 
  65.                     break; 
  66.                 } 
  67.                 //9、进行绘制 
  68.                 g.drawString(text, x, y); 
  69.                 x += getWatermarkLength(text,g); 
  70.                 y += font.getSize() + interval; 
  71.             } 
  72.             // 10、释放资源 
  73.             g.dispose(); 
  74.             // 11、生成图片 
  75.             ImageIO.write(buffImg, "png", new File(targetImgPath)); 
  76.             System.out.println("图片完成添加水印文字"); 
  77.         } catch (Exception e) { 
  78.             e.printStackTrace(); 
  79.         } 
  80.     } 
  81.  
  82.     /** 
  83.      * 计算填充的水印长度 
  84.      * @param text 
  85.      * @param g 
  86.      * @return 
  87.      */ 
  88.     private static int getWatermarkLength(String text, Graphics2D g) { 
  89.         return g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(), 0, text.length()); 
  90.     } 
  91.  
  92.     public static void main(String[] args) { 
  93.         String srcImgPath = "/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址 
  94.         String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy-full.jpg"; //目标文件地址 
  95.         String text = "复 印 无 效"; //水印文字内容 
  96.         Color color = Color.red; //水印文字颜色 
  97.         Font font = new Font("宋体", Font.BOLD, 30); //水印文字字体 
  98.         float alpha = 0.4f; //水印透明度 
  99.         int startWidth = 30; //水印横向位置坐标 
  100.         Integer degree = -0; //水印旋转角度 
  101.         Integer interval = 100; //水印的位置 
  102.         //给图片添加文字水印 
  103.         fullMarkImage(srcImgPath, targetImgPath, text, color, font, alpha, startWidth, degree, interval); 
  104.     } 

2.2、给图像添加图片水印

某些情况下,我们还需要给图像添加图片水印,例如下图效果!

处理过程也很简单!

  1. import org.apache.commons.lang3.StringUtils; 
  2.  
  3. import javax.imageio.ImageIO; 
  4. import javax.swing.*; 
  5. import java.awt.*; 
  6. import java.awt.image.BufferedImage; 
  7. import java.io.File; 
  8.  
  9. /** 
  10.  * 给图像添加水印 
  11.  * @author pzblog 
  12.  * @since 2021-10-29 
  13.  */ 
  14. public class ImageIconWaterMarkUtil { 
  15.  
  16.  
  17.  
  18.     /** 
  19.      * 给图像添加多处文字水印 
  20.      * @param srcImgPath     原始文件地址 
  21.      * @param targetImgPath  目标文件地址 
  22.      * @param iconImgPath    水印icon 
  23.      * @param alpha          水印透明度 
  24.      * @param positionWidth  水印横向位置 
  25.      * @param positionHeight 水印纵向位置 
  26.      * @param degree         水印图片旋转角度 
  27.      * @param location       水印的位置,左上角、右上角、左下角、右下角、居中 
  28.      */ 
  29.     public static void fullMarkImage(String srcImgPath, 
  30.                                  String targetImgPath, 
  31.                                  String iconImgPath, 
  32.                                  float alpha, 
  33.                                  int positionWidth, 
  34.                                  int positionHeight, 
  35.                                  Integer degree, 
  36.                                  String location) { 
  37.         try { 
  38.             // 1、读取源图片 
  39.             Image srcImg = ImageIO.read(new File(srcImgPath)); 
  40.             int srcImgWidth = srcImg.getWidth(null); 
  41.             int srcImgHeight = srcImg.getHeight(null); 
  42.             BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB); 
  43.  
  44.             // 2、得到画笔对象 
  45.             Graphics2D g = buffImg.createGraphics(); 
  46.             // 3、设置对线段的锯齿状边缘处理 
  47.             g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
  48.             g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0, 0, null); 
  49.             // 4、设置水印旋转 
  50.             if (null != degree) { 
  51.                 g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2); 
  52.             } 
  53.             // 5、设置水印文字透明度 
  54.             g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); 
  55.  
  56.             // 6、水印图片的路径 水印图片一般为gif或者png的,这样可设置透明度 
  57.             ImageIcon imgIcon = new ImageIcon(iconImgPath); 
  58.             // 7、得到Image对象。 
  59.             Image iconImg = imgIcon.getImage(); 
  60.             int iconImgWidth = iconImg.getWidth(null); 
  61.             int iconImgHeight = iconImg.getHeight(null); 
  62.  
  63.             int x = 0, y = 0; 
  64.             if (StringUtils.equals(location, "left-top")) { 
  65.                 x = iconImgWidth; 
  66.                 y = iconImgHeight; 
  67.             } else if (StringUtils.equals(location, "right-top")) { 
  68.                 x = srcImgWidth - iconImgWidth - 30; 
  69.                 y = iconImgHeight; 
  70.             } else if (StringUtils.equals(location, "left-bottom")) { 
  71.                 x += iconImgWidth; 
  72.                 y = buffImg.getHeight() - iconImgHeight; 
  73.             } else if (StringUtils.equals(location, "right-bottom")) { 
  74.                 x = srcImgWidth - iconImgWidth - 30; 
  75.                 y = srcImgHeight - iconImgHeight; 
  76.             } else if (StringUtils.equals(location, "center")) { 
  77.                 x = (srcImgWidth - iconImgWidth) / 2; 
  78.                 y = (srcImgHeight - iconImgHeight) / 2; 
  79.             } else { 
  80.                 //自定义位置 
  81.                 x = positionWidth; 
  82.                 y = positionHeight; 
  83.             } 
  84.             g.drawImage(iconImg, x, y, null); 
  85.             g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); 
  86.             // 10、释放资源 
  87.             g.dispose(); 
  88.             // 11、生成图片 
  89.             ImageIO.write(buffImg, "jpg", new File(targetImgPath)); 
  90.             System.out.println("图片完成添加图片水印文字"); 
  91.         } catch (Exception e) { 
  92.             e.printStackTrace(); 
  93.         } 
  94.     } 
  95.  
  96.     /** 
  97.      * 计算填充的水印长度 
  98.      * @param text 
  99.      * @param g 
  100.      * @return 
  101.      */ 
  102.     private static int getWatermarkLength(String text, Graphics2D g) { 
  103.         return g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(), 0, text.length()); 
  104.     } 
  105.  
  106.     public static void main(String[] args) { 
  107.         String srcImgPath = "/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址 
  108.         String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy-img.jpg"; //目标文件地址 
  109.         String iconImgPath = "/Users/pzblog/Desktop/1.png"; //图片水印地址 
  110.         float alpha = 0.6f; //水印透明度 
  111.         int positionWidth = 320; //水印横向位置坐标 
  112.         int positionHeight = 450; //水印纵向位置坐标 
  113.         Integer degree = 0; //水印旋转角度 
  114.         String location = "center"; //水印的位置 
  115.         //给图片添加文字水印 
  116.         fullMarkImage(srcImgPath, targetImgPath, iconImgPath, alpha, positionWidth, positionHeight, degree, location); 
  117.     } 

三、踩坑点

以上实现都很简单,但是在实际的实现过程中,却发现了一个巨大的坑,如果你用的iphone手机拍摄的,按照以上代码进行添加水印,会发现图像突然变横了!

例如下图是原图:

按照上面添加水印的处理,得到的图像结果如下:

很明显,图像旋转了90度!

通过不同拍摄角度的反复测试,发现拍摄角度正常,但是经过程序处理之后,有些是需要旋转 90/180/270 度才能回正。

如果想要在正确的位置加上水印,就必须先对图像进行旋转回到原有的角度,然后再添加水印!

那问题来了,我们如何获取其旋转的角度呢?

经过查阅资料,对于图像的拍摄角度信息,有一个专业的名词:EXIF,EXIF是 Exchangeable Image File的缩写,这是一种专门为数码相机照片设定的格式。

这种格式可以用来记录数字照片的属性信息,例如相机的品牌及型号、相片的拍摄时间、拍摄时所设置的光圈大小、快门速度、ISO等等信息。除此之外它还能够记录拍摄数据,以及照片格式化方式。

通过它,我们可以得知图像的旋转角度信息!

下面,我们就一起来了解下采用 Java 语言如何读取图像的 EXIF 信息,包括如何根据 EXIF 信息对图像进行调整以适合用户浏览。

首先添加 EXIF 依赖包

  1. <dependency> 
  2.     <groupId>com.drewnoakes</groupId> 
  3.     <artifactId>metadata-extractor</artifactId> 
  4.     <version>2.16.0</version> 
  5. </dependency> 

 

然后读取图像的 EXIF 信息

  1. import com.drew.imaging.ImageMetadataReader; 
  2. import com.drew.imaging.ImageProcessingException; 
  3. import com.drew.metadata.Directory; 
  4. import com.drew.metadata.Metadata; 
  5. import com.drew.metadata.Tag; 
  6.  
  7. import java.io.File; 
  8. import java.io.IOException; 
  9.  
  10. /** 
  11.  * @author pzblog 
  12.  * @since 2021-10-29 
  13.  */ 
  14. public class EXIFTest { 
  15.  
  16.     public static void main(String[] args) throws ImageProcessingException, IOException { 
  17.         Metadata metadata = ImageMetadataReader.readMetadata(new File("/Users/pzblog/Desktop/11.jpeg")); 
  18.  
  19.         for (Directory directory : metadata.getDirectories()) { 
  20.             for (Tag tag : directory.getTags()) { 
  21.                 System.out.println(String.format("[%s] - %s = %s"
  22.                         directory.getName(), tag.getTagName(), tag.getDescription())); 
  23.             } 
  24.             if (directory.hasErrors()) { 
  25.                 for (String error : directory.getErrors()) { 
  26.                     System.err.format("ERROR: %s", error); 
  27.                 } 
  28.             } 
  29.         } 
  30.     } 

输入结果:

  1. [JPEG] - Compression Type = Baseline 
  2. [JPEG] - Data Precision = 8 bits 
  3. [JPEG] - Image Height = 1080 pixels 
  4. [JPEG] - Image Width = 1440 pixels 
  5. [JPEG] - Number of Components = 3 
  6. [JPEG] - Component 1 = Y component: Quantization table 0, Sampling factors 2 horiz/2 vert 
  7. [JPEG] - Component 2 = Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert 
  8. [JPEG] - Component 3 = Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert 
  9. [JFIF] - Version = 1.1 
  10. [JFIF] - Resolution Units = none 
  11. [JFIF] - X Resolution = 72 dots 
  12. [JFIF] - Y Resolution = 72 dots 
  13. [JFIF] - Thumbnail Width Pixels = 0 
  14. [JFIF] - Thumbnail Height Pixels = 0 
  15. [Exif IFD0] - Orientation = Right side, top (Rotate 90 CW) 
  16. [Exif SubIFD] - Exif Image Width = 1440 pixels 
  17. [Exif SubIFD] - Exif Image Height = 1080 pixels 
  18. [ICC Profile] - Profile Size = 548 
  19. [ICC Profile] - CMM Type = appl 
  20. [ICC Profile] - Version = 4.0.0 
  21. [ICC Profile] - Class = Display Device 
  22. [ICC Profile] - Color space = RGB  
  23. [ICC Profile] - Profile Connection Space = XYZ  
  24. [ICC Profile] - Profile Date/Time = 2017:07:07 13:22:32 
  25. [ICC Profile] - Signature = acsp 
  26. [ICC Profile] - Primary Platform = Apple Computer, Inc. 
  27. [ICC Profile] - Device manufacturer = APPL 
  28. [ICC Profile] - XYZ values = 0.964 1 0.825 
  29. [ICC Profile] - Tag Count = 10 
  30. [ICC Profile] - Profile Description = Display P3 
  31. [ICC Profile] - Profile Copyright = Copyright Apple Inc., 2017 
  32. [ICC Profile] - Media White Point = (0.9505, 1, 1.0891) 
  33. [ICC Profile] - Red Colorant = (0.5151, 0.2412, 65536) 
  34. [ICC Profile] - Green Colorant = (0.292, 0.6922, 0.0419) 
  35. [ICC Profile] - Blue Colorant = (0.1571, 0.0666, 0.7841) 
  36. [ICC Profile] - Red TRC = para (0x70617261): 32 bytes 
  37. [ICC Profile] - Chromatic Adaptation = sf32 (0x73663332): 44 bytes 
  38. [ICC Profile] - Blue TRC = para (0x70617261): 32 bytes 
  39. [ICC Profile] - Green TRC = para (0x70617261): 32 bytes 
  40. [Photoshop] - Caption Digest = 212 29 140 217 143 0 178 4 233 128 9 152 236 248 66 126 
  41. [Huffman] - Number of Tables = 4 Huffman tables 
  42. [File Type] - Detected File Type Name = JPEG 
  43. [File Type] - Detected File Type Long Name = Joint Photographic Experts Group 
  44. [File Type] - Detected MIME Type = image/jpeg 
  45. [File Type] - Expected File Name Extension = jpg 
  46. [File] - File Name = 11.jpeg 
  47. [File] - File Size = 234344 bytes 
  48. [File] - File Modified Date = 星期日 十一月 07 20:05:52 +08:00 2021 

其中Orientation标签描述的就是图像旋转的角度。

  1. [Exif IFD0] - Orientation = Right side, top (Rotate 90 CW) 

最后,我们可以通过Orientation信息计算出图像对应的旋转角度。

  1. import com.alibaba.fastjson.JSON; 
  2. import com.drew.imaging.jpeg.JpegMetadataReader; 
  3. import com.drew.metadata.Directory; 
  4. import com.drew.metadata.Metadata; 
  5. import com.drew.metadata.Tag; 
  6.  
  7. import java.io.FileInputStream; 
  8. import java.io.IOException; 
  9. import java.io.InputStream; 
  10.  
  11. /** 
  12.  * @author pzblog 
  13.  * @since 2021-10-29 
  14.  */ 
  15. public class TransferImage { 
  16.  
  17.     public static void main(String[] args) throws IOException { 
  18.         String path = "/Users/pzblog/Desktop/11.jpeg"
  19.         int result = getImgRotateAngle(new FileInputStream(path)); 
  20.         System.out.println(result); 
  21.     } 
  22.  
  23.  
  24.     public static int getImgRotateAngle(InputStream inputStream) { 
  25.         int rotateAngle = 0; 
  26.         try { 
  27.             Metadata metadata = JpegMetadataReader.readMetadata(inputStream); 
  28.             Iterable<Directory> directories = metadata.getDirectories(); 
  29.             for (Directory directory : directories) { 
  30.                 for (Tag tag : directory.getTags()) { 
  31.                     System.out.println(JSON.toJSONString(tag)); 
  32.  
  33.                     int tagType = tag.getTagType(); 
  34.                     //照片拍摄角度信息 
  35.                     if (274 == tagType) { 
  36.                         String description = tag.getDescription(); 
  37.                         //Left side, bottom (Rotate 270 CW) 
  38.                         switch (description) { 
  39.                             //顺时针旋转90度 
  40.                             case "Right side, top (Rotate 90 CW)"
  41.                                 rotateAngle = 90; 
  42.                                 break; 
  43.                             case "Left side, bottom (Rotate 270 CW)"
  44.                                 rotateAngle = 270; 
  45.                                 break; 
  46.                             case "Bottom, right side (Rotate 180)"
  47.                                 rotateAngle = 180; 
  48.                                 break; 
  49.                             default
  50.                                 rotateAngle = 0; 
  51.                                 break; 
  52.                         } 
  53.                     } 
  54.  
  55.                 } 
  56.             } 
  57.             return rotateAngle; 
  58.         } catch (Exception e) { 
  59.             return 0; 
  60.         } 
  61.     } 

输出的旋转角度结果:

  1. 90 

接着通过旋转角度参数,对图像进行回正

  1. import javax.imageio.ImageIO; 
  2. import java.awt.*; 
  3. import java.awt.image.BufferedImage; 
  4. import java.io.File; 
  5. import java.io.IOException; 
  6.  
  7. /** 
  8.  * @author panzhi 
  9.  * @since 2021-10-29 
  10.  */ 
  11. public class RotateImage { 
  12.  
  13.     public static BufferedImage rotate(Image src, int angel) { 
  14.         int src_width = src.getWidth(null); 
  15.         int src_height = src.getHeight(null); 
  16.         // calculate the new image size 
  17.         Rectangle rect_des = calcRotatedSize(new Rectangle(new Dimension( 
  18.                 src_width, src_height)), angel); 
  19.  
  20.         BufferedImage res = null
  21.         res = new BufferedImage(rect_des.width, rect_des.height, 
  22.                 BufferedImage.TYPE_INT_RGB); 
  23.         Graphics2D g2 = res.createGraphics(); 
  24.         // transform 
  25.         g2.translate((rect_des.width - src_width) / 2, 
  26.                 (rect_des.height - src_height) / 2); 
  27.         g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2); 
  28.  
  29.         g2.drawImage(src, nullnull); 
  30.         return res; 
  31.     } 
  32.  
  33.     public static Rectangle calcRotatedSize(Rectangle src, int angel) { 
  34.         // if angel is greater than 90 degree, we need to do some conversion 
  35.         if (angel >= 90) { 
  36.             if(angel / 90 % 2 == 1){ 
  37.                 int temp = src.height; 
  38.                 src.height = src.width; 
  39.                 src.width = temp
  40.             } 
  41.             angel = angel % 90; 
  42.         } 
  43.  
  44.         double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2; 
  45.         double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r; 
  46.         double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2; 
  47.         double angel_dalta_width = Math.atan((double) src.height / src.width); 
  48.         double angel_dalta_height = Math.atan((double) src.width / src.height); 
  49.  
  50.         int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha 
  51.                 - angel_dalta_width)); 
  52.         int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha 
  53.                 - angel_dalta_height)); 
  54.         int des_width = src.width + len_dalta_width * 2; 
  55.         int des_height = src.height + len_dalta_height * 2; 
  56.         return new java.awt.Rectangle(new Dimension(des_width, des_height)); 
  57.     } 
  58.  
  59.     public static void main(String[] args) throws IOException { 
  60.         BufferedImage src = ImageIO.read(new File("/Users/pzblog/Desktop/11.jpeg")); 
  61.         BufferedImage des = RotateImage.rotate(src, 90); 
  62.         ImageIO.write(des, "jpg", new File("/Users/pzblog/Desktop/11-rotate.jpeg")); 
  63.     } 

最后给回正后的图像添加水印

  1. public static void main(String[] args) { 
  2.     String srcImgPath = "/Users/pzblog/Desktop/11-rotate.jpeg"; //原始文件地址 
  3.     String targetImgPath = "/Users/pzblog/Desktop/1-rotate-copy.jpg"; //目标文件地址 
  4.     String text = "复 印 无 效"; //水印文字内容 
  5.     Color color = Color.red; //水印文字颜色 
  6.     Font font = new Font("宋体", Font.BOLD, 60); //水印文字字体 
  7.     float alpha = 0.4f; //水印透明度 
  8.     int positionWidth = 320; //水印横向位置坐标 
  9.     int positionHeight = 450; //水印纵向位置坐标 
  10.     Integer degree = -30; //水印旋转角度 
  11.     String location = "center"; //水印的位置 
  12.     //给图片添加文字水印 
  13.     markImage(srcImgPath, targetImgPath, text, color, font, alpha, positionWidth, positionHeight, degree, location); 

输入结果:

添加水印的结果与预期一致!

四、小结

 

给图像添加水印最坑的地方就上面介绍的那个位置,如果是网络截图的照片,基本添加的结果与预期一致,但是采用手机拍摄的,很有可能会发生旋转,因此需要采用一些手法,先获取对应的图像旋转角度,然后进行回正,最后添加水印,保证与预期结果一致!

 

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2020-08-12 07:41:39

SQL 优化语句

2022-07-27 08:16:22

搜索引擎Lucene

2022-12-07 08:42:35

2011-01-10 14:41:26

2011-05-03 15:59:00

黑盒打印机

2021-07-14 09:00:00

JavaFX开发应用

2021-08-09 09:45:37

DockerIPv6Linux

2010-07-06 09:38:51

搭建私有云

2010-07-06 09:43:57

搭建私有云

2022-06-06 08:50:40

CIOIT转型

2014-08-08 13:22:54

测试手机站点移动设备

2021-01-19 09:06:21

MysqlDjango数据库

2019-07-06 10:18:07

人工智能

2011-02-22 13:46:27

微软SQL.NET

2021-02-26 11:54:38

MyBatis 插件接口

2021-12-28 08:38:26

Linux 中断唤醒系统Linux 系统

2023-04-26 12:46:43

DockerSpringKubernetes

2022-03-14 14:47:21

HarmonyOS操作系统鸿蒙

2022-01-08 20:04:20

拦截系统调用

2009-06-05 09:49:18

修改.NET Frammscorlib.dl
点赞
收藏

51CTO技术栈公众号