马赛克实现:
代码会遍历图像的像素,根据一定的块大小,将每个块的像素颜色平均化或设置为块中某个像素的颜色,从而实现马赛克效果。
public void mosaic() {
bufferZone();
int x = s;// 代码会遍历图像的像素,根据一定的块大小,将每个块的像素颜色平均化或设置为块中某个像素的颜色,从而实现马赛克效果。
int[][] data = image(fn);
for (int i = 0; i < data.length; i += (15+1.6*x)) {
for (int j = 0; j < data[i].length; j += (15+1.6*x)) {
Color color = new Color(data[i][j]);
bg.setColor(color);
bg.fillRect(i / 2, j/ 2, 15, 15);// 在绘图缓冲区绘制一个矩形,模拟马赛克效果
}
}
//把缓冲区显示在窗体;
g.drawImage(bufferedImage,0,22,j1);
}
灰度:
计算红绿蓝三个颜色通道的平均值作为灰度值,画笔调成该颜色再去绘制像素点
public void grey(){
bufferZone();
int[][] data = image(fn);
for (int i = 0; i < data.length; i++){
for (int j = 0; j < data[i].length; j++){
Color color = new Color(data[i][j]);
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int sum = (red+green+blue)/3;// 计算红绿蓝三个颜色通道的平均值作为灰度值,用该颜色绘制像素点
Color newColor = new Color(sum,sum,sum);
bg.setColor(newColor);//画笔颜色
bg.drawOval(i/2,j/2,1,1);//像素点
}
}
//把缓冲区显示在窗体;
g.drawImage(bufferedImage,0,22,j1);
}
画原图
public void oripic(){//通过bufferZone方法创建图像的缓冲区,然后遍历图像的每个像素点,使用fillOval画出每个像素
bufferZone();// 准备绘图环境
int[][] data = image(fn);//图片放在项目目录下
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
//得到颜色值,画出每一个点
Color c = new Color(data[i][j]);
bg.setColor(c);//画笔颜色
bg.fillOval(i / 2, j /2, 2, 2);//像素点
}
}
//把缓冲区显示在窗体;
g.drawImage(bufferedImage,0,22,j1);
}
二值化:
设定一个阀值,超过的黑色,没过的白色,从而就黑白,即实现二值化
public void binarization() {
bufferZone();
int x = s;// 定义二值化处理的阈值调整因子
int[][] data = image(fn);
int h = data.length;
int w = data[0].length;
for(int i = 0; i < h; i++){
for(int j = 0; j < w; j++){
int p = data[i][j];// 获取当前像素的颜色值
Color color = new Color(p);// 根据颜色值创建颜色对象
int blue = color.getBlue();
if (blue >(70+2*x)){ // 根据蓝色分量与阈值的比较结果设置画笔颜色
bg.setColor(Color.BLACK);
}else{
bg.setColor(Color.WHITE);
}
bg.drawOval(i/2,j/2,1,1);
}
}
//把缓冲区显示在窗体;
g.drawImage(bufferedImage,0,22,j1);
}
放大:
遍历每个像素点,并在原位置周围绘制更大的矩形,实现放大效果。
public void Todouble(){//遍历每个像素点,并在原位置周围绘制更大的矩形,实现放大效果。
bufferZone();
int x = s;
int[][] data = image(fn);//图片放在项目目录下
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
//得到颜色值,画出每一个点
Color c = new Color(data[i][j]);
bg.setColor(c);// 绘制一个矩形,实现图像的放大效果
bg.fillRect(i*(x/2)/2, j *(x/2)/2, x, x);
}
}
g.drawImage(bufferedImage, 0, 22, j1);
}
缩小:
放大同理
public void narrow(){
bufferZone();
int x = s;// 定义缩放因子
int[][] data = image(fn);//图片放在项目目录下
int w= data.length;
int h=data[0].length;
for (int i = 0; i < w; i+=2) {
for (int j = 0; j < h; j+=2) {
//得到颜色值,画出每一个点
Color c = new Color(data[i][j]);
bg.setColor(c);// 绘制一个矩形,宽度和高度为x,实现缩放效果
bg.fillRect(i/(2*x) , j/(2*x), x, x);//通过每隔一定数量的像素点取一个像素,并在缩小后的图像上绘制相应的像素,实现缩小效果。
}
}
g.drawImage(bufferedImage, 0, 22, j1);
}
油画效果
在我看来就是把马赛克的划分像素再小一点,实现油彩笔拖动画画的效果
public void oil(){
bufferZone();
int x = s;
int[][] data = image(fn);
for (int i =0; i < data.length-5; i +=5){// 遍历图像,跳过一定数量的像素点以模拟油画的笔触
for (int j =0; j < data[i].length-5; j +=5) {
Color color = new Color(data[i][j]);
bg.setColor(color);
Random ran=new Random();// 生成随机大小的椭圆,模拟油画的笔触效果
int r1 = ran.nextInt(20)*x+5;
int r2 = ran.nextInt(40)*x+5;
bg.fillOval((i+x)/2, (j+x)/2,Math.max(r1,r2), Math.min(r1,r2));
}
}
//把缓冲区显示在窗体;
g.drawImage(bufferedImage,0,22,j1);
}
哈哈镜:
根据鼠标点击的位置,计算每个像素点相对于点击点的距离和角度,然后根据这些值调整像素点的新位置
public void funhouseMirror() {//根据鼠标点击的位置,计算每个像素点相对于点击点的距离和角度,然后根据这些值调整像素点的新位置
bufferZone();
int x = s;
int[][] data = image(fn);
int h=data.length;
int w=data[0].length;
int maxDist = (int) Math.sqrt((w/2 - a) * (w/2 - a) + (h/2 - b) * (h/2 - b));
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
int deltaX = i - b;
int deltaY = j - a;
double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
int newX = (int) (i + deltaX * x * maxDist / distance*0.1);
int newY = (int) (j + deltaY * x * maxDist / distance*0.1);
if (newX >= 0 && newX < data.length && newY >= 0 && newY < data[i].length) {
Color color = new Color(data[newX][newY]);
bg.setColor(color);
bg.drawOval(i/2, j/2, 1, 1);
}
}
}
// 把缓冲区显示在窗体;
g.drawImage(bufferedImage, 0, 22, j1);
}
这里解释一下:
计算位移量: deltaX * x * maxDist / distance * 0.1 和 deltaY * x * maxDist / distance * 0.1 计算了像素应移动的距离。
比例因子: (maxDist / distance) 用于调整移动量,使得离点击点更远的像素点移动更多。这样就实现了拉伸效果。
远离点击点的像素:对于离点击点更远的像素,distance 较大,但 (maxDist / distance) 比例较小;然而,由于 deltaX 和 deltaY 的值较大,这些像素点的移动幅度仍然会很大。
靠近点击点的像素:对于离点击点更近的像素,distance 较小,而 (maxDist / distance) 比例较大,但由于 deltaX 和 deltaY 的值较小,这些像素点的移动幅度较小。
放大或缩小: 由于公式中乘以了 x 和 0.1,拉伸或压缩效果可以被调节。
当 x 增大时,拉伸效果会变得更显著。
当 x 减小时(接近于0),拉伸效果会减少,直到完全没有变形
加密
就是输入的askII添加到蓝色阈的最后两位
最后附上源码
package meiyan0811complete; import javax.imageio.ImageIO;//用于读取和写入图片 import javax.swing.*;//构建图形用户界面 import java.awt.*;//处理组件和画图 import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage;//处理图像缓冲区 import java.io.File; import java.io.IOException; import java.util.Random; import java.util.Scanner; public class DrawMouse implements ActionListener,MouseListener { public Graphics g;// 绘制图形 private Graphics bg;//背景绘制 private BufferedImage bufferedImage; public String str;//存储按钮点击事件的字符串 public JPanel j1=null; public int s=1 ;//用于存储滑动条的值,默认为1 public int a,b;// 鼠标点击位置的x,y坐标 public String fn = "D:\\桌面\\11.jpg"; public void mouseClicked(MouseEvent e){ int x =a= e.getX(); int y =b= e.getY(); } public void mousePressed(MouseEvent e){} public void mouseReleased(MouseEvent e){} public void mouseEntered(MouseEvent e){} public void mouseExited(MouseEvent e){} public void actionPerformed(ActionEvent e) {//处理按钮点击 String btnStr = e.getActionCommand();//获取输入的命令 str = btnStr; System.out.println("用户点击了" + btnStr+"按钮......"); System.out.println("稍等一会,图片效果马上呈现"); // 使用SwingWorker来执行异步图像处理,是Java Swing库中的一个类 // 它提供了一种在后台线程中执行长时间运行的任务并更新Swing用户界面的方法。 SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { protected Void doInBackground() { // 根据按钮选择的效果处理图像 //根据字符串绘制相关图片 switch (btnStr) { case "原图": oripic(); break; case "灰度"://取rgb平均值,则画成灰度图 grey(); break; case "二值化": binarization(); break; case "马赛克": mosaic(); break; case"放大": Todouble(); break; case"缩小": narrow(); break; case"油画": oil(); break; case"哈哈镜": funhouseMirror(); break; case "旋转": spin(); break; case "加密": encrypt(); break; } return null; } protected void done() { g.drawImage(bufferedImage, 0, 22, j1);// 将处理后的图像绘制到JPanel上 } }; worker.execute();//启动一个后台任务,该任务在 doInBackground() 中执行图像处理,完成后在 done() 中更新UI } //程序的执行过程:代码 > jvm > os > 总线 > 显示器 //创建缓冲区 把所有的像素点显示在缓存去上 public void bufferZone() { int[][] data = image(fn);// 将图片文件转换为int型的二维数组 bufferedImage = new BufferedImage( data.length, data[0].length,BufferedImage.TYPE_INT_RGB); bg = bufferedImage.getGraphics(); bg.setColor(Color.WHITE); bg.fillRect(0,0,bufferedImage.getWidth(),bufferedImage.getHeight()); } public void oripic(){//通过bufferZone方法创建图像的缓冲区,然后遍历图像的每个像素点,使用fillOval画出每个像素 bufferZone();// 准备绘图环境 int[][] data = image(fn);//图片放在项目目录下 for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { //得到颜色值,画出每一个点 Color c = new Color(data[i][j]); bg.setColor(c);//画笔颜色 bg.fillOval(i / 2, j /2, 2, 2);//像素点 } } //把缓冲区显示在窗体; g.drawImage(bufferedImage,0,22,j1); } public void grey(){ bufferZone(); int[][] data = image(fn); for (int i = 0; i < data.length; i++){ for (int j = 0; j < data[i].length; j++){ Color color = new Color(data[i][j]); int red = color.getRed(); int green = color.getGreen(); int blue = color.getBlue(); int sum = (red+green+blue)/3;// 计算红绿蓝三个颜色通道的平均值作为灰度值,用该颜色绘制像素点 Color newColor = new Color(sum,sum,sum); bg.setColor(newColor);//画笔颜色 bg.drawOval(i/2,j/2,1,1);//像素点 } } //把缓冲区显示在窗体; g.drawImage(bufferedImage,0,22,j1); } public void mosaic() { bufferZone(); int x = s;// 代码会遍历图像的像素,根据一定的块大小,将每个块的像素颜色平均化或设置为块中某个像素的颜色,从而实现马赛克效果。 int[][] data = image(fn); for (int i = 0; i < data.length; i += (15+1.6*x)) { for (int j = 0; j < data[i].length; j += (15+1.6*x)) { Color color = new Color(data[i][j]); bg.setColor(color); bg.fillRect(i / 2, j/ 2, 15, 15);// 在绘图缓冲区绘制一个矩形,模拟马赛克效果 } } //把缓冲区显示在窗体; g.drawImage(bufferedImage,0,22,j1); } public void binarization() { bufferZone(); int x = s;// 定义二值化处理的阈值调整因子 int[][] data = image(fn); int h = data.length; int w = data[0].length; for(int i = 0; i < h; i++){ for(int j = 0; j < w; j++){ int p = data[i][j];// 获取当前像素的颜色值 Color color = new Color(p);// 根据颜色值创建颜色对象 int blue = color.getBlue(); if (blue >(70+2*x)){ // 根据蓝色分量与阈值的比较结果设置画笔颜色 bg.setColor(Color.BLACK); }else{ bg.setColor(Color.WHITE); } bg.drawOval(i/2,j/2,1,1); } } //把缓冲区显示在窗体; g.drawImage(bufferedImage,0,22,j1); } public void narrow(){ bufferZone(); int x = s;// 定义缩放因子 int[][] data = image(fn);//图片放在项目目录下 int w= data.length; int h=data[0].length; for (int i = 0; i < w; i+=2) { for (int j = 0; j < h; j+=2) { //得到颜色值,画出每一个点 Color c = new Color(data[i][j]); bg.setColor(c);// 绘制一个矩形,宽度和高度为x,实现缩放效果 bg.fillRect(i/(2*x) , j/(2*x), x, x);//通过每隔一定数量的像素点取一个像素,并在缩小后的图像上绘制相应的像素,实现缩小效果。 } } g.drawImage(bufferedImage, 0, 22, j1); } public void Todouble(){//遍历每个像素点,并在原位置周围绘制更大的矩形,实现放大效果。 bufferZone(); int x = s; int[][] data = image(fn);//图片放在项目目录下 for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { //得到颜色值,画出每一个点 Color c = new Color(data[i][j]); bg.setColor(c);// 绘制一个矩形,实现图像的放大效果 bg.fillRect(i*(x/2)/2, j *(x/2)/2, x, x); } } g.drawImage(bufferedImage, 0, 22, j1); } public void oil(){ bufferZone(); int x = s; int[][] data = image(fn); for (int i =0; i < data.length-5; i +=5){// 遍历图像,跳过一定数量的像素点以模拟油画的笔触 for (int j =0; j < data[i].length-5; j +=5) { Color color = new Color(data[i][j]); bg.setColor(color); Random ran=new Random();// 生成随机大小的椭圆,模拟油画的笔触效果 int r1 = ran.nextInt(20)*x+5; int r2 = ran.nextInt(40)*x+5; bg.fillOval((i+x)/2, (j+x)/2,Math.max(r1,r2), Math.min(r1,r2)); } } //把缓冲区显示在窗体; g.drawImage(bufferedImage,0,22,j1); } public void funhouseMirror() {//根据鼠标点击的位置,计算每个像素点相对于点击点的距离和角度,然后根据这些值调整像素点的新位置 bufferZone(); int x = s; int[][] data = image(fn); int h=data.length; int w=data[0].length; int maxDist = (int) Math.sqrt((w/2 - a) * (w/2 - a) + (h/2 - b) * (h/2 - b)); for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { int deltaX = i - b; int deltaY = j - a; double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); int newX = (int) (i + deltaX * x * maxDist / distance*0.1); int newY = (int) (j + deltaY * x * maxDist / distance*0.1); if (newX >= 0 && newX < data.length && newY >= 0 && newY < data[i].length) { Color color = new Color(data[newX][newY]); bg.setColor(color); bg.drawOval(i/2, j/2, 1, 1); } } } // 把缓冲区显示在窗体; g.drawImage(bufferedImage, 0, 22, j1); } //对于每个字符的信息,程序会获取其ASCII码值,并且将这个值的最低两位与蓝色分量的最低两位进行位运算,从而隐藏信息。 //遍历每个像素点,将输入的文本信息的ASCII码值隐藏在像素点的蓝色通道的最低2位,然后使用修改后的颜色值绘制像素点。 public void encrypt(){ bufferZone(); int[][] data = image(fn); System.out.println("请输入所要加入图像里的信息:"); Scanner scanner = new Scanner(System.in); String messageToEncrypt = scanner.nextLine(); scanner.close(); int messageIndex = 0; for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { if (messageIndex < messageToEncrypt.length()) { Color color = new Color(data[i][j]); int red = color.getRed(); int green = color.getGreen(); int blue = color.getBlue(); char charToHide = messageToEncrypt.charAt(messageIndex); int asciiValue = (int) charToHide; // 将ASCII码值存储到蓝色通道的最低2位 blue = (blue & 0xFC) | ((asciiValue >> 6) & 0x03); Color newColor = new Color(red, green, blue); bg.setColor(newColor); bg.drawOval(i / 2, j / 2, 1, 1); messageIndex++; } else { // 如果已经加密完整个信息,则将原图像像素写入缓冲区 Color color = new Color(data[i][j]); bg.setColor(color); bg.drawOval(i / 2, j / 2, 1, 1); } } } // 把缓冲区显示在窗体; g.drawImage(bufferedImage, 0, 22, j1); try { File outputImageFile = new File("encrypted_image.jpg"); // 新文件的路径 ImageIO.write(bufferedImage, "jpg", outputImageFile); System.out.println("加密后的图像已保存到文件 'encrypted_image.jpg'"); } catch (IOException e) { System.out.println("保存图像文件时出现错误: " + e.getMessage()); } } public void spin(){ bufferZone(); int x = s; int[][] data = image(fn); } //将一张图片转化一个int型的二维数组 private int[][] image(String imageName) { File file = new File(fn); BufferedImage bi = null; try { bi = ImageIO.read(file); //从文件到图片对象 } catch (Exception e) {//catch块用于捕获并处理try块中抛出的任何异常。这里的Exception e表示捕获所有类型的异常 e.printStackTrace(); } int w = bi.getWidth(); //图片的宽 int h = bi.getHeight(); //图片的高 int[][] imIndex = new int[w][h];//存像素值的二维数组 for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { imIndex[i][j] = bi.getRGB(i, j); //i,j 位置的 Color 值,每个像素点的 color 存入数组 } } return imIndex; } }
package meiyan0811complete; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; public class ImageBoard extends JFrame { //private DrawMouse dl=null; String fName; DrawMouse mouse = new DrawMouse(); public void initUI() { //窗体的创建 this.setTitle("PC版美颜相机--by 张宇超"); this.setSize(1000, 600); this.getContentPane().setBackground(Color.WHITE); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); this.setLayout(new BorderLayout()); //按钮面板 对象JPanel 默认是流式布局 JPanel eastPanel = new JPanel(); eastPanel.setBackground(new Color(220,226,248)); eastPanel.setPreferredSize(new Dimension(200,0)); this.add(eastPanel,BorderLayout.EAST); //图画面板 JPanel drawPanel = new JPanel(); drawPanel.setBackground(Color.WHITE); drawPanel.setPreferredSize(new Dimension(800,500)); this.add(drawPanel,BorderLayout.CENTER); mouse.j1=drawPanel; drawPanel.addMouseListener(mouse); this.setVisible(true); Graphics g=drawPanel.getGraphics(); mouse.g = g; String[] strs={"原图","二值化","灰度","马赛克","放大","缩小","油画","哈哈镜","旋转","加密"}; Color primaryColor = new Color(137, 190, 178); Color textColor = Color.WHITE; // 应用颜色方案到按钮 Font buttonFont = new Font("宋体", Font.PLAIN, 16); // 设置按钮字体和字号 for(int i=0;i< strs.length;i++) { JButton buDraw = new JButton(strs[i]); // 对于字符串数组strs中的每一个字符串,创建一个新的JButton对象。 buDraw.setPreferredSize(new Dimension(80,30));//按钮大小 buDraw.setBackground(primaryColor);//背景颜色 buDraw.setForeground(textColor);//按钮前景颜色 buDraw.setFont(buttonFont);//应用字体 buDraw.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15)); //创建一个空白边框,为按钮的内容留出一些空间,使其看起来更加整洁和美观。 eastPanel.add(buDraw);//按钮添加到面板里 buDraw.addActionListener(mouse);//动作监听器 } JLabel j1 = new JLabel("大小/程度选择:");//显示文本的label j1.setFont(new Font("黑体",1,15)); eastPanel.add(j1); JMenuBar jmb = new JMenuBar();//菜单栏 JMenu jm = new JMenu();//菜单栏里面的菜单项 jm.setText("文件"); jmb.add(jm); this.setJMenuBar(jmb); JMenuItem open = new JMenuItem();//菜单项 open.setText("Open"); jm.add(open); //按钮键->按钮功能(动作监听器) open.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e != null){ JFileChooser jfc =new JFileChooser(); jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);//创建文件选择器 jfc.showDialog(new Label(),"选中"); File file = jfc.getSelectedFile(); fName = file.getPath(); mouse.fn = fName; } } }); //拉杆,滑动条可能用于控制图像处理的强度、大小或其他可调节的参数 JSlider js1 = new JSlider(1,10);// 创建一个滑动条,范围从1到10 js1.setMajorTickSpacing(1);// 设置主刻度之间的间距为1。 js1.setMinorTickSpacing(1);// 设置次刻度之间的间距为1。 js1.setPreferredSize(new Dimension(200,80)); js1.setFont (new Font ("黑体",1,15)); js1.setPaintLabels(true); js1.setPaintTicks(true); js1.setSnapToTicks(true); js1.setOpaque(false); js1.setValue(1); eastPanel.add(js1); js1.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { g.setColor(Color.WHITE); g.fillRect(0,100,1000,1000); if(e != null){ int x =js1.getValue(); mouse.s = x; } } }); } public void paint(Graphics g) { super.paint(g); if (mouse.fn != null){ switch (mouse.str){ case "原图": mouse.oripic(); break; case "灰度": mouse.grey(); break; case "二值化": mouse.binarization(); break; case "马赛克": mouse.mosaic(); break; case "放大": mouse.Todouble(); break; case "缩小": mouse.narrow(); break; case "油画": mouse.oil(); break; case"哈哈镜": mouse.funhouseMirror(); break; case"旋转": mouse.spin(); break; case"加密": mouse.encrypt(); break; default: break; } } } public static void main(String[] args) { new ImageBoard().initUI(); } } 注释也很详细