油画滤镜的基本原理
油画滤镜的基本思想是通过改变图像的像素,将每个像素用周围随机选择的像素来代替,从而产生类似油画笔触的效果。这种处理方式可以模糊图像的细节,使得图像的色块更加连贯,从而模仿油画的艺术效果。
核心步骤
- 局部颜色随机替换:在图像中,每个像素点用其周围的某个随机像素的颜色代替,以模拟油画中笔刷带来的随机性和纹理。
- 多线程并发处理:使用Java的多线程技术同时处理多个区域的像素点,提升处理大图像的效率。
实现油画滤镜的Java代码
接下来是具体的实现代码,使用了Java的图像处理库BufferedImage
,并通过多线程加快图像的处理速度。
代码示例
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultiThreadedOilPaintEffect {
public static void main(String[] args) throws IOException {
// 读取输入图像
BufferedImage inputImage = ImageIO.read(new File("原始图片"));
// 创建文件夹保存图像
String outputDir = "保存路径";
File dir = new File(outputDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(10); // 假设4个线程
// 遍历 radius 从 1 到 8
for (int radius = 2; radius <= 2; radius++) {
// 遍历 intensityLevel 从 50 到 300,步进为 10
for (int intensityLevel = 24; intensityLevel <= 50; intensityLevel += 4) {
int finalRadius = radius;
int finalIntensityLevel = intensityLevel;
// 提交任务到线程池
executor.submit(() -> {
try {
processAndSaveImage(inputImage, finalRadius, finalIntensityLevel, outputDir);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
// 关闭线程池
executor.shutdown();
}
// 处理图像并保存
private static void processAndSaveImage(BufferedImage inputImage, int radius, int intensityLevel, String outputDir) throws IOException {
// 将图像转换为油画效果
BufferedImage outputImage = applyOilPaintEffect(inputImage, radius, intensityLevel);
// 保存结果图像到桌面指定文件夹
String outputFilePath = outputDir + radius + "-" + intensityLevel + ".png";
ImageIO.write(outputImage, "png", new File(outputFilePath));
System.out.println("已保存:" + outputFilePath);
}
// 应用油画效果
private static BufferedImage applyOilPaintEffect(BufferedImage inputImage, int radius, int intensityLevel) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage outputImage = new BufferedImage(width, height, inputImage.getType());
// 将彩色图像转换为灰度图像
int[][] grayImage = new int[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Color color = new Color(inputImage.getRGB(x, y));
int gray = (int) (color.getRed() * 0.299 + color.getGreen() * 0.587 + color.getBlue() * 0.114);
grayImage[x][y] = (gray * intensityLevel) / 255;
}
}
// 应用油画效果
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int[] intensityCounter = new int[intensityLevel + 1];
int[] sumR = new int[intensityLevel + 1];
int[] sumG = new int[intensityLevel + 1];
int[] sumB = new int[intensityLevel + 1];
for (int i = x - radius; i <= x + radius; i++) {
for (int j = y - radius; j <= y + radius; j++) {
if (i >= 0 && i < width && j >= 0 && j < height) {
int intensity = grayImage[i][j];
intensityCounter[intensity]++;
Color color = new Color(inputImage.getRGB(i, j));
sumR[intensity] += color.getRed();
sumG[intensity] += color.getGreen();
sumB[intensity] += color.getBlue();
}
}
}
// 找到最大频率的灰度值
int maxCount = 0;
int selectedIntensity = 0;
for (int k = 0; k <= intensityLevel; k++) {
if (intensityCounter[k] > maxCount) {
maxCount = intensityCounter[k];
selectedIntensity = k;
}
}
// 计算该强度下的平均颜色
int avgR = sumR[selectedIntensity] / maxCount;
int avgG = sumG[selectedIntensity] / maxCount;
int avgB = sumB[selectedIntensity] / maxCount;
// 设置输出图像的像素颜色
outputImage.setRGB(x, y, new Color(avgR, avgG, avgB).getRGB());
}
}
return outputImage;
}
}
可以根据不同的radius和intensityLevel参数调整生成范围,从而筛选出自己最想要的那种图片
这里我生成了7长图片,效果如下:
原始图
生成图:
如果再加大radius则会保留更少细节,根据自己实际情况调整参数去动态生成即可