美颜相机1.0
基本功能介绍
如下图所示:(功能待完善)
基本实现类
1.ImageUI(界面显示)
该类主要实现的功能为:
1. 窗体显示
2. 面板添加
3. 按钮添加及显示
4. 菜单栏的添加及显示
public class ImageUI extends JFrame {
//监听器对象,整个程序中只有一个
ImageListener imgl =new ImageListener();
//添加面板 // 自己写一个类 继承JPanel 替换面板
ImagePanel imgshowPanel = new ImagePanel();
/**
* 初始化界面
* 创建窗体
* 给监听器中 传入 绘制面板对象
* 从监听器对象中拿到ImageArray 对象 传入 绘制面板中
*/
public void showUI() {
//初始化界面
this.setTitle("美颜相机");
this.setSize(1500,750);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//添加面板
JPanel btnjPanel= new JPanel();
//主动调用repaint方法 让窗体 和画板 自己 画出图片
//尺寸
Dimension dm1 = new Dimension(300,0);
btnjPanel.setPreferredSize(dm1);
//设置背景颜色
imgshowPanel.setBackground(Color.LIGHT_GRAY);
btnjPanel.setBackground(new Color(220,220,220));
//将面板添加到窗体上
this.add(btnjPanel,BorderLayout.EAST);
this.add(imgshowPanel,BorderLayout.CENTER);
//添加按钮,封装成方法,传入的是面板对象
this.addbutton(btnjPanel);
// //添加滑杠
// JSlider jSlider = new JSlider();
// jSlider.setPreferredSize(new Dimension(100,100));
// btnjPanel.add(jSlider);
//菜单栏
this.setJMenuBar(initMenu());
this.setVisible(true);
//赋值传递,传递的都是对象的应用地址
//将监听器中初始化好的imageArray 数组对象 传入面板中
imgl.setShowImagePanel(imgshowPanel);
}
public JMenuBar initMenu() {
String[] menus= {
"文件","编辑","导航","工具","视图","帮助"
};
String[] menuFiles = {
"新建","打开","设置","保存","刷新","退出"
};
String[] menuTools = {
"图像滤镜工具","图像绘制工具"
};
String[] imageFunction = {
"灰度","美白","二值化","红色通道","绿色通道","蓝色通道","饱和度"
,"色调","锐化","高斯模糊"
};
String[] imageFunction2 = {
"直线","矩形","椭圆","圆"
};
//菜单栏
JMenuBar menuBar = new JMenuBar();
//创建菜单
for(int i=0;i<menus.length;i++) {
JMenu menu = new JMenu(menus[i]);
if (i==0) {//文件按钮
for (int j = 0; j < menuFiles.length; j++) {
JMenuItem menuItem = new JMenuItem(menuFiles[j]);
menu.add(menuItem);
}
}//工具按钮
else if (i==3) {
for (int j = 0; j < menuTools.length; j++) {
JMenu menu1 = new JMenu(menuTools[j]);
if (j==0) {
for (int k = 0; k < imageFunction.length; k++) {
JMenuItem menuItem1 = new JMenuItem(imageFunction[k]);
menu1.add(menuItem1);
menuItem1.addActionListener(imgl);
}
}
else {
for (int k = 0; k < imageFunction2.length; k++) {
JMenuItem menuItem2 = new JMenuItem(imageFunction2[k]);
menu1.add(menuItem2);
menuItem2.addActionListener(imgl);
}
}
menu.add(menu1);
}
}
menuBar.add(menu);
}
return menuBar;
}
public void addbutton(JPanel btnJPanel) {
String[] name = { "原图","美白", "怀旧", "马赛克", "噪声","去色" ,"油画","二值化","撤回","打开","保存"};
for (int i = 0; i < name.length; i++) {
JButton jButton = new JButton(name[i]);
jButton.setPreferredSize(new Dimension(120,35) );
//给按钮添加监听器
jButton.addActionListener(imgl);
//添加到面板
btnJPanel.add(jButton);
}
}
public static void main(String[] args) {
ImageUI image = new ImageUI();
image.showUI();
image.initMenu();
}
}
2.ImagePanel(重绘面板类)
- 通过继承JPanel 类实现面板的重绘功能,因为图片的覆盖性,所以每次重绘只需绘制出最后一张图片即可
- 所以通过存储图片的二维数组,找到最后一张图片的索引,即
int imgIndex= imageArray.getSize()-1;
再取出图片,调用画笔重绘功能即可
public class ImagePanel extends JPanel{
private ImageArray imageArray;
/**
* 设置ImageArray 的传值方法
* 第一次点击按钮时
* @param imageArray
*/
public void setImageArray(ImageArray imageArray) {
this.imageArray = imageArray;
}
@Override
public void paint(Graphics g) {
super.paint(g);
//涉及数据结构索引操作 一定先确定0是否越界
//绘制 栈顶 因为图片是相互覆盖的 , 所以运用栈的思想 取出图片
//先确定最后一张存入的图片的位置 - 定位
if (imageArray==null) {
return;
}
int imgIndex= imageArray.getSize()-1;
if (imgIndex<0) {
return;//不执行
}
//取出图片对象
BufferedImage img = imageArray.get(imgIndex);
//绘制
g.drawImage(img,(int)(this.getWidth()*0.1),(int)(this.getHeight()*0.1),
(int)(this.getWidth()*0.8),(int)(this.getHeight()*0.8),null);
//将一个小矩形 放置在一个大矩形中 要居中 需要计算小矩形左上角的坐标在大矩形的哪里
}
}
3.ImageListener (监听器)
基本功能实现:
- 初始化得到图片的二维数组
- 通过点击按钮实现监听功能,并调用响应的方法绘制出图片
public class ImageListener extends MouseAdapter implements ActionListener {
/**
* 绘制面板
*/
private ImagePanel showImagePanel;
/**
* 图像绘制工具
*/
private Graphics gr;
/**
* 图像处理工具 可以调用图像处理工具的方法
*/
private ImageUtils imageUtils = new ImageUtils();
/**
* 当前操作的缓存图片对象
*/
private BufferedImage bufferedImage = null;
/**
* 当前加载的图片 对应的图像数组
*/
private int [][] imagrr = {};
/**
* 初始化 缓存图片对象 / 图片数组
*/
{//代码块 初始化(创建对象时自动调用)图片的转化
bufferedImage=imageUtils.getImage("./imgs/3.jpg");
imagrr =imageUtils.getImageArray(bufferedImage);
}
//声明存储图像线性表 数组 初始化是定容量的 不方便扩容
BufferedImage[] buffimgList = new BufferedImage[100];
/**
* 存储图片的动态数组
*/
private ImageArray imageArray = new ImageArray(1000);
/**
* 设置 绘制面板
* @param showImagePanel
*/
public void setShowImagePanel(ImagePanel imagePanel) {
this.showImagePanel = imagePanel;
}
/**
* 动作监听器的方法
* 获取 图形类型按钮的字符串
* 选择图形特效类 对应的方法来执行绘制
* 注意 if(){} else if(){}
* if(){} if(){}
* @param e
*/
public void actionPerformed(ActionEvent e) {
String action = e.getActionCommand();
if (gr==null) { //gr 第一次为null时, 初始赋值
//获取Graphics对象
gr=showImagePanel.getGraphics();
showImagePanel.setImageArray(imageArray);
}
//获取吃力之后的图片
//存入数组中
drawImage(action);
//刷新绘制
showImagePanel.repaint();
}
/**
* 处理图片选择流程
* @param action 按钮字符串
*/
public void drawImage(String action) {
if (action.equals("原图")) {
BufferedImage img=imageUtils.drawImage(imagrr, gr);
imageArray.add(img);
} else if (action.equals("马赛克")) {
BufferedImage img =imageUtils.drawImage03(imagrr, gr);
imageArray.add(img);
} else if (action.equals("二值化")) {
BufferedImage img =imageUtils.drawImage02(imagrr, gr);
imageArray.add(img);
} else if (action.equals("去色")) {
BufferedImage img =imageUtils.drawImage01(imagrr, gr);
imageArray.add(img);
} else if (action.equals("噪声")) {
BufferedImage img =imageUtils.drawImage04(imagrr, gr);
imageArray.add(img);
}
}
}
4.ImageUtils(图像处理类)
该类主要实现了:
- 从文件获取图片,并返回图片对象
- 将得到的图片对象转化为一个图片二维数组,二维数组里面保存图片的像素(ts:从图片获取像素,有两种方法,一:遍历图片的二维数组,直接调用 对象.getRGB(); 获取像素 )
- 通过图片二维数组绘制出缓存图片(绘制在缓存图片上), 最后通过面板主动刷新功能,实现重绘
- 图片处理的功能,比如:去色,二值化,油画,怀旧等功能(ts:为了增加绘制图片的速度,采用了BufferedImage 这个缓存图片数组类,传入图片的二维数组,即可在缓存图片中把所需图片先绘制出来,最后在调用原画笔直接将整张图片绘制出来)具体实现如下:
BufferedImage buffimg = new BufferedImage(imagrr.length, imagrr[0].length,BufferedImage.TYPE_INT_RGB);
Graphics buffg = buffimg.getGraphics();
for (int i = 0; i < imagrr.length; i += 10) {
for (int j = 0; j < imagrr[i].length; j += 10) {
// 取出照片的像素
int i1 = imagrr[i][j];
Color color = new Color(i1);
buffg.setColor(color);
buffg.fillRect(i, j, 10, 10);
}
}
//g.drawImage(buffimg, 0, 0, null);
return buffimg;
完整代码:
public class ImageUtils {
/* 从文件里读取缓冲图片,并返回一个缓冲图片对象
* 1. 创建一个文件类对象,实现从磁盘获取图片
* 2.声明一个缓冲图片对象 ,采用BufferedImage类
* 3.通过对象调用ImageIO.read(file)方法读入图片
* 4. 返回BufferedImage对象img
*/
//构建方法两明确:1.返回值类型2.是否需要参数,参数类型,参数个数
//弄清每个构建方法的目的,最后保持真个逻辑思维明确
public BufferedImage getImage(String path) {
//根据文件路径 创建一个文件对象
File file = new File(path);
//System.out.println(file.getPath());
//先声明一个缓冲图片img
BufferedImage img = null;
try {
img=ImageIO.read(file);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//返回img对象
return img;
}
/*将缓冲图片 转化为 一个二维数组,并取出二维数组里面的像素
* 1.定义方法:返回值类型为 二维数组 ,参数是 BufferedImage对象 img
* 2.用缓冲图片对象来创建一个二维数组
* 3.
*//*
* public BufferedImage getImage(String path) ImageIO.read(file)
* BufferedImage img 要求传入一个文件路径,返回一个缓冲图片对象
* public int[][] getImageArray(BufferedImage img)
* 要求传入一个 BufferedImage img 对象,以获取图片的数组以及像素 img.getRGBbu
* 返回带有像素的二维数组
*/
public int[][] getImageArray(BufferedImage img){
//创建一个二维数组
int[][] imagrr = new int [img.getWidth()][img.getHeight()];
//获取缓存图片的Graphics 绘制的内容
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
//将图片数据保存到数组中
imagrr[i][j]=rgb;
}
}
return imagrr;
}
public BufferedImage drawImage(int[][] imagrr, Graphics g) {
BufferedImage buffimg = new BufferedImage(imagrr.length, imagrr[0].length, BufferedImage.TYPE_INT_ARGB);
Graphics buffg = buffimg.getGraphics();
for (int i = 0; i < imagrr.length; i++) {
for (int j = 0; j < imagrr[i].length; j++) {
int rgb = imagrr[i][j];
Color color =new Color(rgb);
buffg.setColor(color);
buffg.fillRect(i, j, 1, 1);
}
}
// g.drawImage(buffimg, 0, 0, null);
return buffimg;
}
/*
1.首先遍历照片得到一个照片像素的二维数组
2.将得到的像素拆分为三原色
3.操作三原色值实现滤镜
ps:滤镜实际上是对明暗度的调整
*/
//去色:本质上是对RGB三原色 的 递增 或 递减 就能实现 去色操作
public BufferedImage drawImage01(int[][] imagrr,Graphics g) {
//目的:加快绘制速度 思路:
//创建一个空的缓存图片对象,通过缓存图片的画笔,将所需图片的像素直接绘制到缓存图片上,最后直接通过原画笔直接绘制出整张缓存图片
BufferedImage buffimg = new BufferedImage(imagrr.length,imagrr[0].length,BufferedImage.TYPE_INT_ARGB);
Graphics buffg = buffimg.getGraphics();
for (int i = 0; i < imagrr.length; i++) {
for (int j = 0; j <imagrr[i].length; j++) {
//取出照片的像素
int i1 = imagrr[i][j];
//拆分为三原色
int red = (i1>>16)&0xFF;
int green = (i1>>8)&0xFF;
int blue = (i1>>0)&0xFF;
int gray = (red+green+blue)/3;
//灰度化图片
Color color = new Color(gray,gray,gray);
// g.setColor(color);
// g.fillRect(i,j,1,1);
buffg.setColor(color);
buffg.fillRect(i,j,1,1);
}
}
// g.drawImage(buffimg, 0,0,null);
return buffimg;
}
//二值化
public BufferedImage drawImage02(int[][] imagrr,Graphics g) {
BufferedImage buffimg = new BufferedImage(imagrr.length, imagrr[0].length, BufferedImage.TYPE_INT_ARGB);
Graphics buffg = buffimg.getGraphics();
for (int i = 0; i < imagrr.length; i++) {
for (int j = 0; j < imagrr[i].length; j++) {
//取出照片的像素
int i1 = imagrr[i][j];
//拆分为三原色
int red = (i1>>16)&0xFF;
int green = (i1>>8)&0xFF;
int blue = (i1>>0)&0xFF;
// ps: 明暗度 有三原色 等值 递减 或 递加
// ps: 当red = green = blue 时,整个照片会失去色彩
//二值化 抠图
int gray = (red+green+blue)/3;
if (gray>100) {
buffg.setColor(Color.BLACK);
//g.setColor(Color.BLACK);
}else {
//g.setColor(Color.WHITE);
buffg.setColor(Color.WHITE);
}
buffg.fillRect (i, j , 1, 1);
}
}
//g.drawImage(buffimg, 0, 0, null);
return buffimg;
}
//马赛克
public BufferedImage drawImage03(int[][] imagrr, Graphics g) {
BufferedImage buffimg = new BufferedImage(imagrr.length, imagrr[0].length,BufferedImage.TYPE_INT_RGB);
Graphics buffg = buffimg.getGraphics();
for (int i = 0; i < imagrr.length; i += 10) {
for (int j = 0; j < imagrr[i].length; j += 10) {
// 取出照片的像素
int i1 = imagrr[i][j];
Color color = new Color(i1);
buffg.setColor(color);
buffg.fillRect(i, j, 10, 10);
}
}
//g.drawImage(buffimg, 0, 0, null);
return buffimg;
}
//随机绘制彩色噪声矩阵
public BufferedImage drawImage04(int[][] imagrr, Graphics g) {
BufferedImage buffimg = new BufferedImage(imagrr.length, imagrr[0].length, BufferedImage.TYPE_INT_ARGB);
Graphics buffg = buffimg.getGraphics();
Random random = new Random();
for (int i = 0; i < imagrr.length; i++) {
for (int j = 0; j < imagrr[i].length; j++) {
Color color = new Color(random.nextInt(Integer.MAX_VALUE));
buffg.setColor(color);
buffg.fillRect( i, j, 1, 1);
}
}
// g.drawImage(buffimg, 0,0,null);
return buffimg;
}
}
5.ImageArray(动态存储数组类)
基本介绍:
- 用来存储图片的数组 , 方便 我们对每张图片的操作
- 用来存储图片的动态数组类,可以实现数组自动扩容
- 存储的图片对象类型时: BufferedImage
- 目前实现了:
- add方法
- get方法
- remove方法
- size方法
import java.awt.image.BufferedImage;
/**
* 这是一个用来存储图片的动态数组类,可以实现数组自动扩容
* 存储的图片对象类型时: BufferedImage
* 目前实现了:
* add方法
* get方法
* remove方法
* size方法
*
* @author DELL
* @since 2022-04-05
* @version 1.0
*/
public class ImageArray {
/**
* 用来存储图片的底层数组对象
* 初始化为{ },避免出现空指针异常
*/
private BufferedImage[] imgArray = {};
/**
* 数组当前的空间容量
*/
private int length ;
/**
* 数组默认初始化容量
*/
private static final int defaultLength=10;
/*
* 小标 以及 存储的元素个数
* 一个变量 两个作用
*/
private int size;
/**
* 获取数组的元素个数,用来记录数组中元素的个数,即操作次数
* @return 数组中的元素个数
*/
public int getSize() {
return size;
}
/**
* 图片动态数组的初始化构造方法
* 初始化 数组
* 构造方法 创建对象时对用
* 永远不要相信别人给你的参数
* @param initSize 初始化的容量
*/
public ImageArray(int initSize) {
if (initSize<10) {
length=defaultLength;
imgArray=new BufferedImage[length];//动态数组
size=0;
}
//根据参数初始化
if (initSize>10) {
length=initSize;
imgArray=new BufferedImage[length];
size=0;
}
}
//添加图片
public void add(BufferedImage img) {
if (size>=length) {//判断数组空间需不需要扩容
//扩容分两步:
//第一:扩大数组长度
//第二:数据迁移
int oldlength = length;
int newlength = oldlength+(oldlength>>1);//扩容1.5倍
length = newlength;
BufferedImage[] newArray =new BufferedImage[length];
//将原数组中的数据转移至新数组中
for(int i =0;i<oldlength;i++) {
newArray[i]= imgArray[i];
}
//替换引用
imgArray = newArray ;
newArray=null;
}
imgArray[size++]=img;
}
/**
* 获取一张图片
* @param index
* 注意index的合法性
* @return
*/
public BufferedImage get(int index) {
BufferedImage buffimg = imgArray[index];
return buffimg;
}
/**
* 删除
* 删除后 后面的数据需要迁移 ,并且size要-1
* @param index
*/
public void remove(int index) {
if (index>length-1) {
System.out.println("你需要删除的数据不存在");
}
else {
imgArray[index]=null;
size--;
}
}
}
后续功能还待完善~version1.0