开发中业务操作相关的记录-Java给图像加水印-《Java笔记》

admin 2025-10-19 03:22:32 编程 来源:ZONE.CI 全球网 0 阅读模式

Java

一、介绍

在实际的系统开发中,某些业务场景下,经常需要给原始图片添加水印,以防止图片信息在互联网上随意传播!也有的基于当下的业务需求,需要给相机照片加水印、地理位置、时间等信息,以方便记录自己的生活!有的人可能很容易想到,通过 PS 技术就可以很轻松的完成!的确,对于单个图像而言很容易,但是对于成千上万的图像,采用人工处理,显然不可取!问题来了,面对大批量的图像加水印需求,应当如何处理呢?试想一下,如果采用人工方式来给图像添加水印,大概的步骤离不开以下几步:1、先获取需要处理的图像2、然后将图像摆放整齐,用尺子计算出需要加水印的位置3、采用画笔准确无误的在对应的位置上画上水印4、最后,水印添加之后!如果采用程序来实现,思路也是一样的,废话也不多说了,代码直接撸上!

二、程序实践

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

import%20org.apache.commons.lang3.StringUtils;import%20javax.imageio.ImageIO;import%20java.awt.*;import%20java.awt.image.BufferedImage;import%20java.io.File;/**%20*%20给图像添加水印%20*%20@since%202021-10-29%20*/public%20class%20ImageWaterMarkUtil%20{%20%20%20%20/**%20%20%20%20%20*%20给图像添加文字水印%20%20%20%20%20*%20@param%20srcImgPath%20%20%20%20%20原始文件地址%20%20%20%20%20*%20@param%20targetImgPath%20%20目标文件地址%20%20%20%20%20*%20@param%20text%20%20%20%20%20%20%20%20%20%20%20水印内容%20%20%20%20%20*%20@param%20color%20%20%20%20%20%20%20%20%20%20水印文字颜色%20%20%20%20%20*%20@param%20font%20%20%20%20%20%20%20%20%20%20%20水印文字字体%20%20%20%20%20*%20@param%20alpha%20%20%20%20%20%20%20%20%20%20水印透明度%20%20%20%20%20*%20@param%20positionWidth%20%20水印横向位置%20%20%20%20%20*%20@param%20positionHeight%20水印纵向位置%20%20%20%20%20*%20@param%20degree%20%20%20%20%20%20%20%20%20水印图片旋转角度%20%20%20%20%20*%20@param%20location%20%20%20%20%20%20%20水印的位置,左上角、右上角、左下角、右下角、居中%20%20%20%20%20*/%20%20%20%20public%20static%20void%20markImage(String%20srcImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20targetImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20text,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Color%20color,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Font%20font,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20float%20alpha,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20positionWidth,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20positionHeight,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Integer%20degree,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20location)%20{%20%20%20%20%20%20%20%20try%20{%20%20%20%20%20%20%20%20%20%20%20%20//%201、读取源图片%20%20%20%20%20%20%20%20%20%20%20%20Image%20srcImg%20=%20ImageIO.read(new%20File(srcImgPath));%20%20%20%20%20%20%20%20%20%20%20%20int%20srcImgWidth%20=%20srcImg.getWidth(null);%20%20%20%20%20%20%20%20%20%20%20%20int%20srcImgHeight%20=%20srcImg.getHeight(null);%20%20%20%20%20%20%20%20%20%20%20%20BufferedImage%20buffImg%20=%20new%20BufferedImage(srcImgWidth,%20srcImgHeight,%20BufferedImage.TYPE_INT_RGB);%20%20%20%20%20%20%20%20%20%20%20%20//%202、得到画笔对象%20%20%20%20%20%20%20%20%20%20%20%20Graphics2D%20g%20=%20buffImg.createGraphics();%20%20%20%20%20%20%20%20%20%20%20%20//%203、设置对线段的锯齿状边缘处理%20%20%20%20%20%20%20%20%20%20%20%20g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,%20RenderingHints.VALUE_INTERPOLATION_BILINEAR);%20%20%20%20%20%20%20%20%20%20%20%20g.drawImage(srcImg.getScaledInstance(srcImgWidth,%20srcImgHeight,%20Image.SCALE_SMOOTH),%200,%200,%20null);%20%20%20%20%20%20%20%20%20%20%20%20//%204、设置水印旋转%20%20%20%20%20%20%20%20%20%20%20%20if%20(null%20!=%20degree)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20g.rotate(Math.toRadians(degree),%20(double)%20buffImg.getWidth()%20/%202,%20(double)%20buffImg.getHeight()%20/%202);%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20//%205、设置水印文字颜色%20%20%20%20%20%20%20%20%20%20%20%20g.setColor(color);%20%20%20%20%20%20%20%20%20%20%20%20//%206、设置水印文字Font%20%20%20%20%20%20%20%20%20%20%20%20g.setFont(font);%20%20%20%20%20%20%20%20%20%20%20%20//%207、设置水印文字透明度%20%20%20%20%20%20%20%20%20%20%20%20g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,%20alpha));%20%20%20%20%20%20%20%20%20%20%20%20//%208、水印图片的位置%20%20%20%20%20%20%20%20%20%20%20%20int%20x%20=%200,%20y%20=%200;%20%20%20%20%20%20%20%20%20%20%20%20if%20(StringUtils.equals(location,%20"left-top"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%2030;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20font.getSize();%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"right-top"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20srcImgWidth%20-%20getWatermarkLength(text,%20g)%20-%2030;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20font.getSize();%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"left-bottom"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20+=%2030;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20buffImg.getHeight()%20-%20font.getSize();%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"right-bottom"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20srcImgWidth%20-%20getWatermarkLength(text,%20g)%20-%2030;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20srcImgHeight%20-%20font.getSize();%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"center"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20(srcImgWidth%20-%20getWatermarkLength(text,%20g))%20/%202;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20srcImgHeight%20/%202;%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//自定义位置%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20positionWidth;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20positionHeight;%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20//%209、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y)%20%20%20%20%20%20%20%20%20%20%20%20g.drawString(text,%20x,%20y);%20%20%20%20%20%20%20%20%20%20%20%20//%2010、释放资源%20%20%20%20%20%20%20%20%20%20%20%20g.dispose();%20%20%20%20%20%20%20%20%20%20%20%20//%2011、生成图片%20%20%20%20%20%20%20%20%20%20%20%20ImageIO.write(buffImg,%20"png",%20new%20File(targetImgPath));%20%20%20%20%20%20%20%20%20%20%20%20System.out.println("图片完成添加水印文字");%20%20%20%20%20%20%20%20}%20catch%20(Exception%20e)%20{%20%20%20%20%20%20%20%20%20%20%20%20e.printStackTrace();%20%20%20%20%20%20%20%20}%20%20%20%20}%20%20%20%20/**%20%20%20%20%20*%20计算填充的水印长度%20%20%20%20%20*%20@param%20text%20%20%20%20%20*%20@param%20g%20%20%20%20%20*%20@return%20%20%20%20%20*/%20%20%20%20private%20static%20int%20getWatermarkLength(String%20text,%20Graphics2D%20g)%20{%20%20%20%20%20%20%20%20return%20g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(),%200,%20text.length());%20%20%20%20}%20%20%20%20public%20static%20void%20main(String[]%20args)%20{%20%20%20%20%20%20%20%20String%20srcImgPath%20=%20"/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址
  • String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy.jpg"; //目标文件地址
  • String text = "复 印 无 效"; //水印文字内容
  • Color color = Color.red; //水印文字颜色
  • Font font = new Font("宋体", Font.BOLD, 60); //水印文字字体
  • float alpha = 0.4f; //水印透明度
  • int positionWidth = 320; //水印横向位置坐标
  • int positionHeight = 450; //水印纵向位置坐标
  • Integer degree = -30; //水印旋转角度
  • String location = "center"; //水印的位置
  • //给图片添加文字水印
  • markImage(srcImgPath, targetImgPath, text, color, font, alpha, positionWidth, positionHeight, degree, location);
  • }
  • }
  • 运行结果如下:水印添加成功!

    2.1、给图像添加多处文字

    有的需求会要求给图像添加多处文字水印,例如下图!处理过程也很简单!

    import%20javax.imageio.ImageIO;import%20java.awt.*;import%20java.awt.image.BufferedImage;import%20java.io.File;/**%20*%20给图像添加水印%20*%20@since%202021-10-29%20*/public%20class%20ImageFullWaterMarkUtil%20{%20%20%20%20/**%20%20%20%20%20*%20给图像添加多处文字水印%20%20%20%20%20*%20@param%20srcImgPath%20%20%20%20%20原始文件地址%20%20%20%20%20*%20@param%20targetImgPath%20%20目标文件地址%20%20%20%20%20*%20@param%20text%20%20%20%20%20%20%20%20%20%20%20水印内容%20%20%20%20%20*%20@param%20color%20%20%20%20%20%20%20%20%20%20水印文字颜色%20%20%20%20%20*%20@param%20font%20%20%20%20%20%20%20%20%20%20%20水印文字字体%20%20%20%20%20*%20@param%20alpha%20%20%20%20%20%20%20%20%20%20水印透明度%20%20%20%20%20*%20@param%20startWidth%20%20%20%20%20水印横向起始位置%20%20%20%20%20*%20@param%20degree%20%20%20%20%20%20%20%20%20水印图片旋转角度%20%20%20%20%20*%20@param%20interval%20%20%20%20%20%20%20高度间隔%20%20%20%20%20*/%20%20%20%20public%20static%20void%20fullMarkImage(String%20srcImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20targetImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20text,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Color%20color,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Font%20font,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20float%20alpha,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20startWidth,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Integer%20degree,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Integer%20interval)%20{%20%20%20%20%20%20%20%20try%20{%20%20%20%20%20%20%20%20%20%20%20%20//%201、读取源图片%20%20%20%20%20%20%20%20%20%20%20%20Image%20srcImg%20=%20ImageIO.read(new%20File(srcImgPath));%20%20%20%20%20%20%20%20%20%20%20%20int%20srcImgWidth%20=%20srcImg.getWidth(null);%20%20%20%20%20%20%20%20%20%20%20%20int%20srcImgHeight%20=%20srcImg.getHeight(null);%20%20%20%20%20%20%20%20%20%20%20%20BufferedImage%20buffImg%20=%20new%20BufferedImage(srcImgWidth,%20srcImgHeight,%20BufferedImage.TYPE_INT_RGB);%20%20%20%20%20%20%20%20%20%20%20%20//%202、得到画笔对象%20%20%20%20%20%20%20%20%20%20%20%20Graphics2D%20g%20=%20buffImg.createGraphics();%20%20%20%20%20%20%20%20%20%20%20%20//%203、设置对线段的锯齿状边缘处理%20%20%20%20%20%20%20%20%20%20%20%20g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,%20RenderingHints.VALUE_INTERPOLATION_BILINEAR);%20%20%20%20%20%20%20%20%20%20%20%20g.drawImage(srcImg.getScaledInstance(srcImgWidth,%20srcImgHeight,%20Image.SCALE_SMOOTH),%200,%200,%20null);%20%20%20%20%20%20%20%20%20%20%20%20//%204、设置水印旋转%20%20%20%20%20%20%20%20%20%20%20%20if%20(null%20!=%20degree)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20g.rotate(Math.toRadians(degree),%20(double)%20buffImg.getWidth()%20/%202,%20(double)%20buffImg.getHeight()%20/%202);%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20//%205、设置水印文字颜色%20%20%20%20%20%20%20%20%20%20%20%20g.setColor(color);%20%20%20%20%20%20%20%20%20%20%20%20//%206、设置水印文字Font%20%20%20%20%20%20%20%20%20%20%20%20g.setFont(font);%20%20%20%20%20%20%20%20%20%20%20%20//%207、设置水印文字透明度%20%20%20%20%20%20%20%20%20%20%20%20g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,%20alpha));%20%20%20%20%20%20%20%20%20%20%20%20//%208、水印图片的位置%20%20%20%20%20%20%20%20%20%20%20%20int%20x%20=%20startWidth;%20%20%20%20%20%20%20%20%20%20%20%20int%20y%20=%20font.getSize();%20%20%20%20%20%20%20%20%20%20%20%20int%20space%20=%20srcImgHeight%20/%20interval;%20%20%20%20%20%20%20%20%20%20%20%20for%20(int%20i%20=%200;%20i%20<%20space;%20i++)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//如果最后一个坐标的y轴比height高,直接退出%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(((y%20+%20font.getSize())%20>%20srcImgHeight)%20||%20((x%20+%20getWatermarkLength(text,g))%20%20>%20srcImgWidth))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//9、进行绘制%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20g.drawString(text,%20x,%20y);%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20+=%20getWatermarkLength(text,g);%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20+=%20font.getSize()%20+%20interval;%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20//%2010、释放资源%20%20%20%20%20%20%20%20%20%20%20%20g.dispose();%20%20%20%20%20%20%20%20%20%20%20%20//%2011、生成图片%20%20%20%20%20%20%20%20%20%20%20%20ImageIO.write(buffImg,%20"png",%20new%20File(targetImgPath));%20%20%20%20%20%20%20%20%20%20%20%20System.out.println("图片完成添加水印文字");%20%20%20%20%20%20%20%20}%20catch%20(Exception%20e)%20{%20%20%20%20%20%20%20%20%20%20%20%20e.printStackTrace();%20%20%20%20%20%20%20%20}%20%20%20%20}%20%20%20%20/**%20%20%20%20%20*%20计算填充的水印长度%20%20%20%20%20*%20@param%20text%20%20%20%20%20*%20@param%20g%20%20%20%20%20*%20@return%20%20%20%20%20*/%20%20%20%20private%20static%20int%20getWatermarkLength(String%20text,%20Graphics2D%20g)%20{%20%20%20%20%20%20%20%20return%20g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(),%200,%20text.length());%20%20%20%20}%20%20%20%20public%20static%20void%20main(String[]%20args)%20{%20%20%20%20%20%20%20%20String%20srcImgPath%20=%20"/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址
  • String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy-full.jpg"; //目标文件地址
  • String text = "复 印 无 效"; //水印文字内容
  • Color color = Color.red; //水印文字颜色
  • Font font = new Font("宋体", Font.BOLD, 30); //水印文字字体
  • float alpha = 0.4f; //水印透明度
  • int startWidth = 30; //水印横向位置坐标
  • Integer degree = -0; //水印旋转角度
  • Integer interval = 100; //水印的位置
  • //给图片添加文字水印
  • fullMarkImage(srcImgPath, targetImgPath, text, color, font, alpha, startWidth, degree, interval);
  • }
  • }
  • 2.2、给图像添加图片水印

    某些情况下,还需要给图像添加图片水印,例如下图效果!处理过程也很简单!

    import%20org.apache.commons.lang3.StringUtils;import%20javax.imageio.ImageIO;import%20javax.swing.*;import%20java.awt.*;import%20java.awt.image.BufferedImage;import%20java.io.File;/**%20*%20给图像添加水印%20*%20@since%202021-10-29%20*/public%20class%20ImageIconWaterMarkUtil%20{%20%20%20%20/**%20%20%20%20%20*%20给图像添加多处文字水印%20%20%20%20%20*%20@param%20srcImgPath%20%20%20%20%20原始文件地址%20%20%20%20%20*%20@param%20targetImgPath%20%20目标文件地址%20%20%20%20%20*%20@param%20iconImgPath%20%20%20%20水印icon%20%20%20%20%20*%20@param%20alpha%20%20%20%20%20%20%20%20%20%20水印透明度%20%20%20%20%20*%20@param%20positionWidth%20%20水印横向位置%20%20%20%20%20*%20@param%20positionHeight%20水印纵向位置%20%20%20%20%20*%20@param%20degree%20%20%20%20%20%20%20%20%20水印图片旋转角度%20%20%20%20%20*%20@param%20location%20%20%20%20%20%20%20水印的位置,左上角、右上角、左下角、右下角、居中%20%20%20%20%20*/%20%20%20%20public%20static%20void%20fullMarkImage(String%20srcImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20targetImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20iconImgPath,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20float%20alpha,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20positionWidth,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20positionHeight,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Integer%20degree,%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20location)%20{%20%20%20%20%20%20%20%20try%20{%20%20%20%20%20%20%20%20%20%20%20%20//%201、读取源图片%20%20%20%20%20%20%20%20%20%20%20%20Image%20srcImg%20=%20ImageIO.read(new%20File(srcImgPath));%20%20%20%20%20%20%20%20%20%20%20%20int%20srcImgWidth%20=%20srcImg.getWidth(null);%20%20%20%20%20%20%20%20%20%20%20%20int%20srcImgHeight%20=%20srcImg.getHeight(null);%20%20%20%20%20%20%20%20%20%20%20%20BufferedImage%20buffImg%20=%20new%20BufferedImage(srcImgWidth,%20srcImgHeight,%20BufferedImage.TYPE_INT_RGB);%20%20%20%20%20%20%20%20%20%20%20%20//%202、得到画笔对象%20%20%20%20%20%20%20%20%20%20%20%20Graphics2D%20g%20=%20buffImg.createGraphics();%20%20%20%20%20%20%20%20%20%20%20%20//%203、设置对线段的锯齿状边缘处理%20%20%20%20%20%20%20%20%20%20%20%20g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,%20RenderingHints.VALUE_INTERPOLATION_BILINEAR);%20%20%20%20%20%20%20%20%20%20%20%20g.drawImage(srcImg.getScaledInstance(srcImgWidth,%20srcImgHeight,%20Image.SCALE_SMOOTH),%200,%200,%20null);%20%20%20%20%20%20%20%20%20%20%20%20//%204、设置水印旋转%20%20%20%20%20%20%20%20%20%20%20%20if%20(null%20!=%20degree)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20g.rotate(Math.toRadians(degree),%20(double)%20buffImg.getWidth()%20/%202,%20(double)%20buffImg.getHeight()%20/%202);%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20//%205、设置水印文字透明度%20%20%20%20%20%20%20%20%20%20%20%20g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,%20alpha));%20%20%20%20%20%20%20%20%20%20%20%20//%206、水印图片的路径%20水印图片一般为gif或者png的,这样可设置透明度%20%20%20%20%20%20%20%20%20%20%20%20ImageIcon%20imgIcon%20=%20new%20ImageIcon(iconImgPath);%20%20%20%20%20%20%20%20%20%20%20%20//%207、得到Image对象。%20%20%20%20%20%20%20%20%20%20%20%20Image%20iconImg%20=%20imgIcon.getImage();%20%20%20%20%20%20%20%20%20%20%20%20int%20iconImgWidth%20=%20iconImg.getWidth(null);%20%20%20%20%20%20%20%20%20%20%20%20int%20iconImgHeight%20=%20iconImg.getHeight(null);%20%20%20%20%20%20%20%20%20%20%20%20int%20x%20=%200,%20y%20=%200;%20%20%20%20%20%20%20%20%20%20%20%20if%20(StringUtils.equals(location,%20"left-top"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20iconImgWidth;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20iconImgHeight;%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"right-top"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20srcImgWidth%20-%20iconImgWidth%20-%2030;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20iconImgHeight;%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"left-bottom"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20+=%20iconImgWidth;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20buffImg.getHeight()%20-%20iconImgHeight;%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"right-bottom"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20srcImgWidth%20-%20iconImgWidth%20-%2030;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20srcImgHeight%20-%20iconImgHeight;%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20if%20(StringUtils.equals(location,%20"center"))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20(srcImgWidth%20-%20iconImgWidth)%20/%202;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20(srcImgHeight%20-%20iconImgHeight)%20/%202;%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//自定义位置%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%20=%20positionWidth;%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%20=%20positionHeight;%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20g.drawImage(iconImg,%20x,%20y,%20null);%20%20%20%20%20%20%20%20%20%20%20%20g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));%20%20%20%20%20%20%20%20%20%20%20%20//%2010、释放资源%20%20%20%20%20%20%20%20%20%20%20%20g.dispose();%20%20%20%20%20%20%20%20%20%20%20%20//%2011、生成图片%20%20%20%20%20%20%20%20%20%20%20%20ImageIO.write(buffImg,%20"jpg",%20new%20File(targetImgPath));%20%20%20%20%20%20%20%20%20%20%20%20System.out.println("图片完成添加图片水印文字");%20%20%20%20%20%20%20%20}%20catch%20(Exception%20e)%20{%20%20%20%20%20%20%20%20%20%20%20%20e.printStackTrace();%20%20%20%20%20%20%20%20}%20%20%20%20}%20%20%20%20/**%20%20%20%20%20*%20计算填充的水印长度%20%20%20%20%20*%20@param%20text%20%20%20%20%20*%20@param%20g%20%20%20%20%20*%20@return%20%20%20%20%20*/%20%20%20%20private%20static%20int%20getWatermarkLength(String%20text,%20Graphics2D%20g)%20{%20%20%20%20%20%20%20%20return%20g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(),%200,%20text.length());%20%20%20%20}%20%20%20%20public%20static%20void%20main(String[]%20args)%20{%20%20%20%20%20%20%20%20String%20srcImgPath%20=%20"/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址
  • String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy-img.jpg"; //目标文件地址
  • String iconImgPath = "/Users/pzblog/Desktop/1.png"; //图片水印地址
  • float alpha = 0.6f; //水印透明度
  • int positionWidth = 320; //水印横向位置坐标
  • int positionHeight = 450; //水印纵向位置坐标
  • Integer degree = 0; //水印旋转角度
  • String location = "center"; //水印的位置
  • //给图片添加文字水印
  • fullMarkImage(srcImgPath, targetImgPath, iconImgPath, alpha, positionWidth, positionHeight, degree, location);
  • }
  • }
  • 三、踩坑点

    以上实现都很简单,但是在实际的实现过程中,却发现了一个巨大的坑,如果用的iphone手机拍摄的,按照以上代码进行添加水印,会发现图像突然变横了!例如下图是原图:按照上面添加水印的处理,得到的图像结果如下:很明显,图像旋转了90度!通过不同拍摄角度的反复测试,发现拍摄角度正常,但是经过程序处理之后,有些是需要旋转%2090/180/270%20度才能回正。如果想要在正确的位置加上水印,就必须先对图像进行旋转回到原有的角度,然后再添加水印!那问题来了,如何获取其旋转的角度呢?经过查阅资料,对于图像的拍摄角度信息,有一个专业的名词:EXIF,EXIF是%20Exchangeable%20Image%20File的缩写,这是一种专门为数码相机照片设定的格式。这种格式可以用来记录数字照片的属性信息,例如相机的品牌及型号、相片的拍摄时间、拍摄时所设置的光圈大小、快门速度、ISO等等信息。除此之外它还能够记录拍摄数据,以及照片格式化方式。通过它,可以得知图像的旋转角度信息!下面一起来了解下采用%20Java%20语言如何读取图像的%20EXIF%20信息,包括如何根据%20EXIF%20信息对图像进行调整以适合用户浏览。

    • 首先添加%20EXIF%20依赖包

      <dependency>
    • %20%20<groupId>com.drewnoakes</groupId>%20%20<artifactId>metadata-extractor</artifactId>%20%20<version>2.16.0</version></dependency>
    • 然后读取图像的%20EXIF%20信息 ```java import%20com.drew.imaging.ImageMetadataReader; import%20com.drew.imaging.ImageProcessingException; import%20com.drew.metadata.Directory; import%20com.drew.metadata.Metadata; import%20com.drew.metadata.Tag;

    import%20java.io.File; import%20java.io.IOException;

    public%20class%20EXIFTest%20{

    public%20static%20void%20main(String[]%20args)%20throws%20ImageProcessingException,%20IOException%20{%20%20%20%20Metadata%20metadata%20=%20ImageMetadataReader.readMetadata(new%20File("/Users/pzblog/Desktop/11.jpeg"));
  • for (Directory directory : metadata.getDirectories()) {
  • for (Tag tag : directory.getTags()) {
  • System.out.println(String.format("[%s] - %s = %s",
  • directory.getName(), tag.getTagName(), tag.getDescription()));
  • }
  • if (directory.hasErrors()) {
  • for (String error : directory.getErrors()) {
  • System.err.format("ERROR: %s", error);
  • }
  • }
  • }
  • }
  • }

    1. 输出结果:

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

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

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

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

    输出的旋转角度结果:

    1. 90
    • 接着通过旋转角度参数,对图像进行回正 ```java import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;

    public class RotateImage {

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

    }

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

    输入结果:Java给图像加水印 - 图7添加水印的结果与预期一致!

    以太坊cppgolang区别 编程

    以太坊cppgolang区别

    以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
    progolang 编程

    progolang

    Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
    golangn个发送者 编程

    golangn个发送者

    Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
    golang技能图谱 编程

    golang技能图谱

    从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
    评论:0   参与:  4