目录
RGB
图像处理算法
RGB转灰度图像
二值图像
负片
马赛克
复古
锐化
在说Java数字图像处理之前,需要知道以下知识:RGB
RGB
一幅RGB图像有三个通道,也就是RGB,分别代表红、绿、蓝三个通道的颜色,这三个通道颜色的信息组合起来,就成为了多种多样的颜色,比如0,0,0代表的是黑色
在数字图像中,每一个像素点都有一个RGB数值,Java可以用getRGB来获取
这里我给出获取像素RGB信息的一种Java方法
1. 确定图片路径
2. 使用BufferedImage读取确定路径下的图像数据
BufferedImage buf_im= ImageIO.read(new FileInputStream(im_path));
3. 可以使用getWidth等函数读取相关信息
im_width=buf_im.getWidth();
im_height=buf_im.getHeight();
4. 遍历每一个像素点,用getRGB函数获取对应的像素信息
int[][] red = new int[im_width][im_height];
int[][] green = new int[im_width][im_height];
int[][] blue = new int[im_width][im_height];
for(int i=0;i<im_width;i++){
for (int j =0;j<im_height;j++){
red[i][j] = (buf_im.getRGB(i,j)>>16) &0xFF;
green[i][j] = (buf_im.getRGB(i,j)>>8) &0xFF;
blue[i][j] = (buf_im.getRGB(i,j)) &0xFF;
}
}
当然,Java还有很多封装好的包可以直接提供给用户实现读取,这里给出了最基本的逐像素法来读取,因为后面我会用到这些单个的像素进行图像处理
图像处理算法
拿到一幅图像的大小和RGB数据后就能对他们进行处理,通过合适的处理能够给这副图像加上一个滤镜
RGB转灰度图像
RGB抓灰度图像的算法很简单,利用一些已经经过时间检验的公式就能够得出灰度值,将Color的三个通道设置成同样的灰度值即可实现灰度图像
int im_width = info.getIm_width();
int im_height = info.getIm_height();
for(int i=0;i<im_width;i++){
for (int j =0;j<im_height;j++){
int red = info.getIm_red()[i][j];
int green = info.getIm_green()[i][j];
int blue = info.getIm_blue()[i][j];
int grayscale = (red*38+green*75+blue*15)>>7;
g.setColor(new Color(grayscale,grayscale,grayscale));
g.drawLine(i,j,i,j);
}
}
效果如下
二值图像
二值图像说白了就是只有黑色和白色,实现也很简单,对灰度图像的灰度值进行判断,如果小于某一个值则设置成黑0,大于某个值就设置成白255
int im_width = info.getIm_width();
int im_height = info.getIm_height();
for(int i=0;i<im_width;i++){
for (int j =0;j<im_height;j++){
int red = info.getIm_red()[i][j];
int green = info.getIm_green()[i][j];
int blue = info.getIm_blue()[i][j];
int grayscale = (red*38+green*75+blue*15)>>7;
int binary = 255;
if(grayscale < 70){
binary = 0;
}
g.setColor(new Color(binary,binary,binary));
g.drawLine(i,j,i,j);
}
}
效果如下
通过设置不同的阈值,可以实现不同的结果,比如阈值很低时,可以勾勒出描边!
负片
负片的实现也很简单,将得到的RGB数据对应反转即可(255-x)
int im_width = info.getIm_width();
int im_height = info.getIm_height();
for(int i=0;i<im_width;i++){
for (int j =0;j<im_height;j++){
int red = info.getIm_red()[i][j];
int green = info.getIm_green()[i][j];
int blue = info.getIm_blue()[i][j];
red=255-red;
green=255-green;
blue=255-blue;
g.setColor(new Color(red,green,blue));
g.drawLine(i,j,i,j);
}
}
实现效果如下
马赛克
马赛克效果的实现会比前面几种稍复杂些,但是也是基于对像素的处理
马赛克效果说白了就是模糊边界,将一个区域的像素都填充为同一个,做到了涂抹的效果
int im_width = info.getIm_width();
int im_height = info.getIm_height();
for(int i=0;i<im_width-10;i=i+10){
for (int j =0;j<im_height-10;j=j+10){
int red = info.getIm_red()[i][j];
int green = info.getIm_green()[i][j];
int blue = info.getIm_blue()[i][j];
g.setColor(new Color(red,green,blue));
g.fillRect(i,j,10,10);
}
}
单是上面的处理还不够,对于边界还有一部分余量不满足处理步长(上例中为10)的,需要额外进行处理,我采用的是填充左上角的像素颜色
int w=im_width%10;
int h=im_height%10;
//补齐底部余量
for(int i=0;i<im_width-10;i=i+10){
int red = info.getIm_red()[i][im_height-h];
int green = info.getIm_green()[i][im_height-h];
int blue = info.getIm_blue()[i][im_height-h];
g.setColor(new Color(red,green,blue));
g.fillRect(i,im_height-h,10,h);
}
//补齐右侧余量
for(int j=0;j<im_height-10;j=j+10){
int red = info.getIm_red()[im_width-w][j];
int green = info.getIm_green()[im_width-w][j];
int blue = info.getIm_blue()[im_width-w][j];
g.setColor(new Color(red,green,blue));
g.fillRect(im_width-w,j,w,10);
}
实现效果如下
另外,通过修改执行马赛克算法的步长,可以修改马赛克模糊的大小,比如我修改为50,马赛克会变大,当然,图片的内容也就更加模糊了!
复古
复古滤镜也有特定算法,直接给出代码和结果
int im_width = info.getIm_width();
int im_height = info.getIm_height();
for(int i=0;i<im_width;i++){
for (int j =0;j<im_height;j++){
int red = info.getIm_red()[i][j];
int green = info.getIm_green()[i][j];
int blue = info.getIm_blue()[i][j];
int R = (int) (0.393 * red + 0.469 * green + 0.049 * blue);
int G = (int) (0.349 * red + 0.586 * green + 0.068 * blue);
int B = (int) (0.272 * red + 0.534 * green + 0.031 * blue);
g.setColor(new Color(R,G,B));
g.drawLine(i,j,i,j);
}
}
实现效果如下
锐化
锐化效果实际上就是增加不同像素颜色之间的差异,也就是边缘增强效果,这里可以用到卷积来处理,不了解卷积原理的同学可以参照这篇 - 文章
比较简单和常见的卷积核是3*3的只有中心为9,其它为-1的核,我使用的就是这种卷积核,但是要注意卷积边界问题,需要额外处理留下的卷积核大小-1的空隙
这里常见的有两种方法:1. 直接对空出来的地方进行额外处理,填充像素即可;2. 在原始图像补一圈或者多圈‘0’,就能做到全卷积了。
我这里采用直接处理的方法
int im_width = info.getIm_width();
int im_height = info.getIm_height();
for(int i=0;i<im_width-kernel_size+1;i++){
for(int j =0;j<im_height-kernel_size+1;j++){
int red=0,green=0,blue=0;
for(int x=0;x<kernel_size;x++){
for(int y=0;y<kernel_size;y++){
red=red+info.getIm_red()[i+x][j+y]*kernel_sharp[x][y];
green=green+info.getIm_green()[i+x][j+y]*kernel_sharp[x][y];
blue=blue+info.getIm_blue()[i+x][j+y]*kernel_sharp[x][y];
}
}
red=red>255?255:Math.max(0,red);
green=green>255?255:Math.max(0,green);
blue=blue>255?255:Math.max(0,blue);
g.setColor(new Color(red,green,blue));
g.drawLine(i,j,i,j);
//补充底层缺失像素 - 也可以图像外圈使用补0的方法
if(j==(im_height-kernel_size)){
for(int m=0;m<kernel_size-1;m++){
red=info.getIm_red()[i][j+m+1];
green=info.getIm_green()[i][j+m+1];
blue=info.getIm_green()[i][j+m+1];
g.setColor(new Color(red,green,blue));
g.drawLine(i,j+m+1,i,j+m+1);
}
}
}
//补充右侧缺失像素
if(i==(im_width-kernel_size)){
for(int n=0;n<kernel_size-1;n++){
for(int j =0;j<im_height;j++){
int red=info.getIm_red()[i+n+1][j];
int green=info.getIm_green()[i+n+1][j];
int blue=info.getIm_green()[i+n+1][j];
g.setColor(new Color(red,green,blue));
g.drawLine(i+n+1,j,i+n+1,j);
}
}
}
}
实现效果如下