油画滤镜的基本原理
油画滤镜的基本思想是通过改变图像的像素,将每个像素用周围随机选择的像素来代替,从而产生类似油画笔触的效果。这种处理方式可以模糊图像的细节,使得图像的色块更加连贯,从而模仿油画的艺术效果。
核心步骤
- 局部颜色随机替换:在图像中,每个像素点用其周围的某个随机像素的颜色代替,以模拟油画中笔刷带来的随机性和纹理。
- 多线程并发处理:使用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 到 8for (int radius = 2; radius <= 2; radius++) {// 遍历 intensityLevel 从 50 到 300,步进为 10for (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则会保留更少细节,根据自己实际情况调整参数去动态生成即可