油画滤镜的基本原理

油画滤镜的基本思想是通过改变图像的像素,将每个像素用周围随机选择的像素来代替,从而产生类似油画笔触的效果。这种处理方式可以模糊图像的细节,使得图像的色块更加连贯,从而模仿油画的艺术效果。

核心步骤

  1. 局部颜色随机替换:在图像中,每个像素点用其周围的某个随机像素的颜色代替,以模拟油画中笔刷带来的随机性和纹理。
  2. 多线程并发处理:使用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则会保留更少细节,根据自己实际情况调整参数去动态生成即可

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部