马赛克实现:
代码会遍历图像的像素,根据一定的块大小,将每个块的像素颜色平均化或设置为块中某个像素的颜色,从而实现马赛克效果。
java
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);
}
灰度:
计算红绿蓝三个颜色通道的平均值作为灰度值,画笔调成该颜色再去绘制像素点
java
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);
}
画原图
java
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);
}
二值化:
设定一个阀值,超过的黑色,没过的白色,从而就黑白,即实现二值化
java
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);
}
放大:
遍历每个像素点,并在原位置周围绘制更大的矩形,实现放大效果。
java
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);
}
缩小:
放大同理
java
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);
}
油画效果
在我看来就是把马赛克的划分像素再小一点,实现油彩笔拖动画画的效果
java
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);
}
哈哈镜:
根据鼠标点击的位置,计算每个像素点相对于点击点的距离和角度,然后根据这些值调整像素点的新位置
java
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();
}
}
注释也很详细