目录
一.图像处理的原理
二.图像处理的方法
(1)将图片转换成二维数组
(2)绘制原图
(3)绘制灰度图像
(4)绘制马赛克图像
(5)绘制珠纹化图像
(6)绘制融合图像
(7)绘制轮廓图像
(8)绘制黑白版画
(9)绘制锐化图像
(10)绘制高斯模糊图像
三.锐化、均值模糊、浮雕
//rgb数字构成颜色,范围0~255
Color c1 = new Color(123456789);
//int数字构成颜色,int范围即可
Cplor c2 = new Color(111, 222, 111);
图像处理的本质即为对代表图像的二维数组中的数值按照一定的算法进行重新计算。所以我们首先需要把图片转换成二维数组。
/**
*
* @param path 图片路径
* @return imgArr 保存图片像素的二维数组
*/
public int[][] imageFileToArr(String path) {
File file = new File(path);
//将图片保存到图像缓冲区
BufferedImage buffimg = null;
try {
buffimg = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
}
int w = buffimg.getWidth();
int h = buffimg.getHeight();
//存储像素点的二维数组
int[][] imgArr = new int[w][h];
for(int i = 0; i < w; i++) {
for(int j = 0; j < h; j++) {
imgArr[i][j] = buffimg.getRGB(i, j);
}
}
return imgArr;
}
将图片通过图像缓冲区存储到二维数组以后,我们可以通过遍历,将每个像素点打印出来,从而实现绘制原图的功能。
//绘制原图
public void drawImage(int[][] imgArr) {
for(int i = 0; i < imgArr.length; i++) {
for(int j = 0; j < imgArr[0].length; j++) {
int rgb = imgArr[i][j];
Color color = new Color(rgb);
gr.setColor(color);
gr.fillRect(i,j,1,1);
}
}
实现了绘制原图功能的hxd会发现,图像在窗体的打印十分缓慢,这是因为IO与底层GPU速度不匹配造成的,此时我们进行的是一个一个像素的数据传输,导致图片加载缓慢,关于解决方法我会在后面介绍(在计算时将所有的像素点存入BufferedImage对象,进行全内存操作,然后再绘制)。
实现灰度很简单,三原色R、G、B取相同的值即可以实现灰度,我们一般取平均值。
//绘制灰度图像
public void drawGrayImage(int[][] imgArr) {
for(int i = 0; i < imgArr.length; i++) {
for(int j = 0; j < imgArr[0].length; j++) {
int rgb = imgArr[i][j];
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = (rgb >> 0) & 0xFF;
int gray = (red + green + blue) / 3;
Color color = new Color(gray, gray, gray);
gr.setColor(color);
gr.drawLine(i, j, i, j);
}
}
}
也可以通过如下代码来获取R、G、B的值:
Color c = new Color(rgb);
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
马赛克图像的实现则是通过扩大像素点-->画方格,来实现的。
//绘制马赛克图像
public void drawMosaicImage(int[][] imgArr) {
for(int i = 0; i < imgArr.length; i+=16) {
for(int j = 0; j < imgArr[0].length; j+=16) {
Color c2 = new Color(imgArr[i][j]);
//画方格子,实现方格状马赛克
gr.setColor(c2);
gr.fillRect(i, j, 16, 16);
}
}
}
珠纹化则是将绘制方格改成了绘制圆;
//绘制珠纹化图像
public void drawPearlImage(int[][] imgArr) {
for(int i = 0; i < imgArr.length; i+=10) {
for(int j = 0; j < imgArr[0].length; j+=10) {
int rgb = imgArr[i][j];
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = (rgb >> 0) & 0xFF;
Color color = new Color(red, green, blue);
gr.setColor(color);
gr.fillOval(i, j, 8, 8);
}
}
}
将两个图像融合就是将两个图像的RBG值按照一定的比例组成一个新的RGB的值;
//绘制融合图像
public void drawMixImage(int[][] imgArr, int[][] imgArr2) {
int w = Math.min(imgArr.length, imgArr2.length);
int h = Math.min(imgArr[0].length, imgArr2[0].length);
double pro_1 = 0.4;
double pro_2 = 0.6;
for(int i = 0; i < w; i++) {
for(int j = 0; j < h; j++) {
int rgb = imgArr[i][j];
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = (rgb >> 0) & 0xFF;
int rgb2 = imgArr2[i][j];
int red2 = (rgb2 >> 16) & 0xFF;
int green2 = (rgb2 >> 8) & 0xFF;
int blue2 = (rgb2 >> 0) & 0xFF;
int newRed = (int)(pro_1*red + pro_2*red2);
int newGreen = (int)(pro_1*green + pro_2*green2);
int newBlue = (int)(pro_1*blue + pro_2*blue2);
Color color = new Color(newRed, newGreen, newBlue);
gr.setColor(color);
gr.fillRect(i, j, 1, 1);
}
}
}
//绘制轮廓图像
public void drawOutlinesImage(int[][] imgArr) {
//-3防止越界
for(int i = 0; i < imgArr.length-3; i++) {
for(int j = 0; j < imgArr[0].length-3; j++) {
int rgb = imgArr[i][j];
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = (rgb >> 0) & 0xFF;
int gray = (red + green + blue) / 3;
// 获取三个单通道的RGB值
int rgb2 = imgArr[i+3][j+3];
int red2 = (rgb2 >> 16) & 0xFF;
int green2 = (rgb2 >> 8) & 0xFF;
int blue2 = (rgb2 >> 0) & 0xFF;
int gray2 = (red2 + green2 + blue2) / 3;
//边缘检测
if(Math.abs(gray-gray2) > 5) {
gr.setColor(Color.WHITE);
} else {
gr.setColor(Color.BLACK);
}
gr.drawLine(i, j, i, j);
}
}
}
rgb值大于某值画黑,小于某值画白;
public void drawBlackWhiteImage(int[][] imgArr) {
for(int i = 0; i < imgArr.length; i++) {
for(int j = 0; j < imgArr[0].length; j++) {
int rgb = imgArr[i][j];
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = (rgb >> 0) & 0xFF;
int gray = (red + green + blue) / 3;
if(gray > 100) {
gr.setColor(Color.WHITE);
} else {
gr.setColor(Color.BLACK);
}
gr.drawLine(i, j, i, j);
}
}
}
高斯模糊与图像卷积滤波,这个博客对于图像卷积相关知识讲得还不错,hxdm可以看看;卷积算法就是通过不同的卷积核来处理图像二维数组,从而获取不同的特征图像;
public void drawSharpenImage(int[][] imgArr) {
//锐化卷积核
float[][] kArr = {
{-1, -1, -1},
{-1, 9, -1},
{-1, -1, -1}
};
//开始卷积计算
//tem保存单次卷积核数组乘积的值
int[][] tem = new int[kArr.length][kArr[0].length];
//valid为卷积后得到的特征图像的保存数组
int validWidth = imgArr[0].length - kArr[0].length + 1;
int validHeihgt = imgArr.length - kArr.length + 1;
int[][] valid = new int[validHeihgt][validWidth];
//卷积
for(int i = 0; i < validHeihgt; i++) {
for(int j = 0; j < validWidth; j++) {
for(int y = 0; y < kArr.length; y++) {
for(int z = 0; z < kArr[0].length; z++) {
tem[y][z] = (int)(imgArr[i+y][j+z] * kArr[y][z]);
}
}
int k = 0;
for(int y = 0; y < kArr.length; y++) {
for(int z = 0; z < kArr[0].length; z++) {
k += tem[y][z];
}
}
if(k < 0) {
k = 0;
}
else if(k > 255){
k = 255;
}
valid[i][j] = (byte)k;
}
}
//打印卷积后的图像
for(int i = 0; i < valid.length; i++) {
for(int j = 0; j < valid[0].length; j++) {
Color c = new Color(valid[i][j]);
gr.setColor(c);
gr.drawLine(i, j, i, j);
}
}
}
此代码我们卷积的对象是保存整个rgb值的二维数组,可以发现,锐化的效果并不理想,作为改进,我们可以把R、G、B三个通道分别卷积然后合成新的图像,效果会更好,后文会讲解。
关于高斯模糊,我们则是通过高斯函数来计算权重矩阵,从而计算模糊值,然后进行高斯模糊。特别要注意边界值的处理,详细情况可以参考(9)中所附链接。
这三种图像处理的方式都是通过不同的卷积核对图像进行卷积。(R、G、B三个通道分别卷积然后合成新的图像)。
如下是他们的卷积核,可以按照需求修改,不同的卷积核会有不同特征,更大的卷积核效果会更明显。
//卷积核
//1.锐化卷积核
float[][] kArr_1 = {
{-1, -1, -1},
{-1, 9, -1},
{-1, -1, -1}
};
//2.浮雕卷积核
float[][] kArr_2 = {
{-1, -1, 0},
{-1, 0, 1},
{0, 1, 1}
};
//3.均值模糊卷积核
float[][] kArr_3 = {
{0, 0.2f, 0},
{0.2f, 0, 0.2f},
{0, 0.2f, 0}
};
RGB三通道分别卷积的代码如下:
/**
*
* @param imgArr 存储图像的二维数组
* @param kArr 卷积核二维数组
* @return buffimg 卷积后的BufferedImage类型图像
*/
public BufferedImage drawConvolutionImage(int[][] imgArr, float[][] kArr) {
BufferedImage buffimg = new BufferedImage(imgArr.length, imgArr[0].length, BufferedImage.TYPE_INT_ARGB);
Graphics bfg = buffimg.getGraphics();
//三个tem分别保存RGB单次卷积核数组乘积的值
int[][] temRed = new int[kArr.length][kArr[0].length];
int[][] temGreen = new int[kArr.length][kArr[0].length];
int[][] temBlue = new int[kArr.length][kArr[0].length];
//三个二维数组分别保存三原色的像素
int[][] redArr = new int[imgArr.length][imgArr[0].length];
int[][] greenArr = new int[imgArr.length][imgArr[0].length];
int[][] blueArr = new int[imgArr.length][imgArr[0].length];
for(int i = 0; i < imgArr.length; i++) {
for(int j = 0; j < imgArr[0].length; j++) {
int rgb = imgArr[i][j];
redArr[i][j] = (rgb >> 16) & 0xFF;
greenArr[i][j] = (rgb >> 8) & 0xFF;
blueArr[i][j] = (rgb >> 0) & 0xFF;
}
}
//三个valid二维数组分别保存卷积后特征图像的rgb值
int validWidth = imgArr[0].length - kArr[0].length + 1;
int validHeihgt = imgArr.length - kArr.length + 1;
int[][] validRed = new int[validHeihgt][validWidth];
int[][] validGreen = new int[validHeihgt][validWidth];
int[][] validBlue = new int[validHeihgt][validWidth];
//卷积
for(int i = 0; i < validHeihgt; i++) {
for(int j = 0; j < validWidth; j++) {
for(int y = 0; y < kArr.length; y++) {
for(int z = 0; z 255){
kRed = 255;
}
if(kGreen < 0) {
kGreen = 0;
}
else if(kGreen > 255){
kGreen = 255;
}
if(kBlue < 0) {
kBlue = 0;
}
else if(kBlue > 255){
kBlue = 255;
}
validRed[i][j] = kRed;
validGreen[i][j] = kGreen;
validBlue[i][j] = kBlue;
}
}
//打印卷积后的图像
for(int i = 0; i < validHeihgt; i++) {
for(int j = 0; j < validWidth; j++) {
Color c = new Color(validRed[i][j], validGreen[i][j], validBlue[i][j]);
bfg.setColor(c);
bfg.drawLine(i, j, i, j);
}
}
//窗体上绘制
gr.drawImage(buffimg, 0, 0, null);
return buffimg;
}