参考了网上别人的代码,实现了图像的哈尔小波变换,可以实现模糊图像的功能,另外将轮廓合并起来可以实现类似粉笔画的功能。
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 哈尔小波变换
* 参考:https://blog.csdn.net/baidu_21578557/article/details/51988006
* https://blog.csdn.net/kezunhai/article/details/47080977
*/
public class HaarTransform {
@Test
public void test() throws IOException {
String fromPic = "C:\\Users\\Dell\\Pictures\\QQ截图20210628085926.jpg";
BufferedImage bufferedImage = ImageIO.read(new File(fromPic));
// 进行哈尔小波变换,可以实现模糊图片的功能
//BufferedImage destImg = haarWavelet(bufferedImage, 32, 2);
// 通过哈尔小波变换获取图像轮廓
BufferedImage destImg = haarWaveletEdge(bufferedImage, 32, 2);
// 将横向,纵向和斜向轮廓叠加,可以实现粉笔画的功能
BufferedImage mergedImg = mergeHaarWavelet(destImg);
File newFile = new File("d:\\test8.jpg");
ImageIO.write(mergedImg, "jpg", newFile);
}
/**
* 哈尔小波变换
* 作用:模糊图像
* @param srcImage
* @param range 2的整数倍,如果为1,就是原图像
* @param difference 过滤阈值,越大图片越模糊,相邻两个像素间的差值
* @return
*/
public BufferedImage haarWavelet(BufferedImage srcImage, int range, double difference) {
int iw = srcImage.getWidth();
int ih = srcImage.getHeight();
int[] pixels = new int[iw * ih];
srcImage.getRGB(0, 0, iw, ih, pixels, 0, iw);
// 赋初值
int w = 1;
int h = 1;
// 计算进行付立叶变换的宽度和高度(2的整数次方)
while (w * 2 <= iw) {
w *= 2;
}
while (h * 2 <= ih) {
h *= 2;
}
// 存储哈尔小波变换结果
double[][] haar = new double[h][w];
// 输出图像
int[] newPixels = new int[h * w];
// 对行进行haar小波变换
for (int i = 0; i < h; i++) {
// 获取一行输入图像
double row[] = new double[w];
for (int j = 0; j < w; j++) {
row[j] = pixels[i * iw + j] & 0xff;
}
haarTransform(row);
haar[i] = row;
}
// 对列进行haar小波变换
for (int i = 0; i < w; i++) {
// 获取一列输入图像
double[] col = new double[h];
for (int j = 0; j < h; j++) {
col[j] = haar[j][i];
}
haarTransform(col);
for (int j = 0; j < h; j++) {
haar[j][i] = col[j];
}
}
// 统计
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int key = (int)Math.abs(haar[i][j]);
if(map.containsKey(key)) {
map.put(key, map.get(key) + 1);
} else {
map.put(key, 1);
}
}
}
for(Map.Entry<Integer, Integer> item : map.entrySet()) {
System.out.println(item.getKey() + ", " + item.getValue());
}
// 进行过滤
for (int i = h/range; i < h; i++) {
for (int j = w/range; j < w; j++) {
if (Math.abs(haar[i][j]) < difference) {
haar[i][j] = 0;
}
}
}
// 还原为图像
// 先对列进行还原
for (int i = 0; i < w; i++) {
// 获取一列输入图像
double col[] = new double[h];
for (int j = 0; j < h; j++) {
col[j] = haar[j][i];
}
iHaarTransform(col);
for (int j = 0; j < h; j++) {
haar[j][i] = col[j];
}
}
// 再对行进行还原
for (int i = 0; i < h; i++) {
// 获取一行输入图像
double row[] = new double[w];
for (int j = 0; j < w; j++) {
row[j] = haar[i][j];
}
iHaarTransform(row);
haar[i] = row;
}
// 生成像素
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int temp = (int)haar[i][j];
newPixels[i * w + j] = (clamp(temp) << 16) | (clamp(temp) << 8) | clamp(temp);
}
}
BufferedImage destImg = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
destImg.setRGB(0, 0, w, h, newPixels, 0, w);
return destImg;
}
/**
* 提取横向,纵向,斜向的边界
* @param srcImage
* @param range
* @param difference
* @return
*/
public BufferedImage haarWaveletEdge(BufferedImage srcImage, int range, double difference) {
int iw = srcImage.getWidth();
int ih = srcImage.getHeight();
int[] pixels = new int[iw * ih];
srcImage.getRGB(0, 0, iw, ih, pixels, 0, iw);
// 赋初值
int w = 1;
int h = 1;
// 计算进行付立叶变换的宽度和高度(2的整数次方)
while (w * 2 <= iw) {
w *= 2;
}
while (h * 2 <= ih) {
h *= 2;
}
// 存储哈尔小波变换结果
double[][] haar = new double[h][w];
// 输出图像
int[] newPixels = new int[h * w];
// 对行进行haar小波变换
for (int i = 0; i < h; i++) {
// 获取一行输入图像
double row[] = new double[w];
for (int j = 0; j < w; j++) {
row[j] = pixels[i * iw + j] & 0xff;
}
haarTransform2(row);
haar[i] = row;
}
// 对列进行haar小波变换
for (int i = 0; i < w; i++) {
// 获取一列输入图像
double[] col = new double[h];
for (int j = 0; j < h; j++) {
col[j] = haar[j][i];
}
haarTransform2(col);
for (int j = 0; j < h; j++) {
haar[j][i] = col[j];
}
}
// 生成像素
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int temp = (int) Math.abs(haar[i][j]);
if (i > h / 2 || j > w / 2) {
//temp += 180;
temp += 60;
}
newPixels[i * w + j] = (clamp(temp) << 16) | (clamp(temp) << 8) | clamp(temp);
}
}
BufferedImage destImg = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
destImg.setRGB(0, 0, w, h, newPixels, 0, w);
return destImg;
}
/**
* 将横向,纵向,斜向的边界线进行合并
* 素描效果
* @param srcImage
* @return
*/
public BufferedImage mergeHaarWavelet(BufferedImage srcImage) {
int w = srcImage.getWidth();
int h = srcImage.getHeight();
int[] pixels = new int[w * h];
srcImage.getRGB(0, 0, w, h, pixels, 0, w);
// 输出图像
int[] newPixels = new int[w * h / 4];
// 生成像素
for (int i = h/2; i < h; i++) {
for (int j = w/2; j < w; j++) {
int rowPixel = pixels[(i - h/2) * w + j] & 0xff;
int colPixel = pixels[i * w + (j - w/2)] & 0xff;
int obliquePixel = pixels[i * w + j] & 0xff;
int temp = rowPixel + colPixel + obliquePixel;
newPixels[(i - h/2) * w / 2 + (j - w/2)] = (clamp(temp) << 16) | (clamp(temp) << 8) | clamp(temp);
}
}
BufferedImage destImg = new BufferedImage(w/2, h/2, BufferedImage.TYPE_BYTE_GRAY);
destImg.setRGB(0, 0, w/2, h/2, newPixels, 0, w/2);
return destImg;
}
/**
* 哈尔小波变换,一直变换到只剩一个像素点
* @param src
*/
public void haarTransform(double[] src) {
int n = src.length;
// 缩小到一个像素
while (n > 1) {
double[] dest = new double[n];
for (int i=0; i<n / 2; i++) {
double a = src[2*i];
double b = src[2*i+1];
dest[i] = (a + b) / 2;
dest[i + n/2] = (a - b) / 2;
}
System.arraycopy(dest, 0, src, 0, n);
n /= 2;
}
}
/**
* 哈尔小波变换,只变换一次
* @param src
*/
public void haarTransform2(double[] src) {
int n = src.length;
// 缩小到一个像素
while (n > 1) {
double[] dest = new double[n];
for (int i=0; i<n / 2; i++) {
double a = src[2*i];
double b = src[2*i+1];
dest[i] = (a + b) / 2;
dest[i + n/2] = (a - b) / 2;
}
System.arraycopy(dest, 0, src, 0, n);
n /= 2;
break;
}
}
/**
* 哈尔小波变换逆变换
* @param src
*/
public void iHaarTransform(double[] src) {
int n = 1;
// 缩小到一个像素
while (n < src.length) {
double[] dest = new double[n*2];
for (int i=0; i<n; i++) {
double a = src[i];
double b = src[i+n];
dest[2*i] = (a + b);
dest[2*i + 1] = (a - b);
}
System.arraycopy(dest, 0, src, 0, n*2);
n *= 2;
}
}
/**
* 如果像素点的值超过了0-255的范围,予以调整
*
* @param value 输入值
* @return 输出值
*/
private int clamp(int value) {
return value > 255 ? 255 : (Math.max(value, 0));
}
/**
* 哈尔小波变换
*/
@Test
public void transform() {
double[] src = new double[]{64,2,3,61,60,6,7,57};
int n = src.length;
// 缩小到一个像素
while (n > 1) {
double[] dest = new double[n];
for (int i=0; i<n / 2; i++) {
double a = src[2*i];
double b = src[2*i+1];
dest[i] = (a + b) / 2;
dest[i + n/2] = (a - b) / 2;
}
System.arraycopy(dest, 0, src, 0, n);
n /= 2;
}
for (double item : src) {
System.out.print(item + ",");
}
}
/**
* 哈尔小波变换逆变换
*/
@Test
public void itransform() {
double[] src = new double[]{32.5,0.0,0.5,0.5,31.0,-29.0,27.0,-25.0};
int n = 1;
// 缩小到一个像素
while (n < src.length) {
double[] dest = new double[n*2];
for (int i=0; i<n; i++) {
double a = src[i];
double b = src[i+n];
dest[2*i] = (a + b);
dest[2*i + 1] = (a - b);
}
System.arraycopy(dest, 0, src, 0, n*2);
n *= 2;
}
for (double item : src) {
System.out.print(item + ",");
}
}
}