简单图像滤镜功能的Java实现

图像滤镜功能Java实现

前置内容

  • RGB图片: 对于图片中的像素, 使用Red, Green, Blue 三种颜色共同得到一个最终的显示颜色, 其数值均在0 - 255 之间

  • 在Java中对于图像的处理可以在使用IO流得到的BufferedImage类的实例操作, 也可以读入矩阵进行操作

  • BufferedImage类实例中会提供一个Graphics实例, 可以直接对BufferedImage实例进行操作


操作前的原图

对单点像素处理的滤镜算法

在这类方法中, 对于每个像素的处理只与这个像素本身有关, 与图片的其他信息无关, 于是可以简单对每个像素进行遍历, 按照预定的方法进行逐个处理

    public void pixelRasterize(BufferedImage bufImg, String op) {
        		Graphics g = bufImg.getGraphics();
            for (int i = 0; i < bufImg.getWidth(); i++) {
                for (int j = 0; j < bufImg.getHeight(); j++) {
                    int pixelVal = bufImg.getRGB(i, j);
                    int red = (pixelVal >> 16) & 0xFF;
                    int green = (pixelVal >> 8) & 0xFF;
                    int blue = pixelVal & 0xFF;
                    Color color = pixelShader(red, green, blue, op);
                    bufImg.setRGB(i, j, color.getRGB());
                }
            }
            this.bufImg = bufImg;
    }

RGB拆分

  • 对于图片中的像素, RGB数值使用一个int来表示, 由于其每种颜色只需要0 - 255, 所以仅使用了 1 byte 进行封装, 最高的8位置1, 次高的每8位依次为Red, Green, Blue

这些功能中都只需要对单个像素进行处理: 灰度化, 二值化, 去除深色背景, 负片, 复古, 明亮

    public Color pixelShader(int red, int green, int blue, String op) {
        switch (op) {
            case "Original":
            case "Second":
                return new Color(red, green, blue);
            case "Gray":
                int gray = (red + green + blue) / 3;
                return new Color(gray, gray, gray);
            case "Binary":
                gray = (red + green + blue) / 3;
                if (gray < 70)
                    return new Color(0, 0, 0);
                else return new Color(255, 255, 255);
            case "Rid Bg":
                gray = (red + green + blue) / 3;
                if (gray > 63)
                    return new Color(red, green, blue);
                else return new Color(225, 225, 225);
            case "Negative":
                red = 255 - red;
                green = 255 - green;
                blue = 255 - blue;
                return new Color(red, green, blue);
            case "OldFashion":
                int R = (int) (0.393 * red + 0.469 * green + 0.049 * blue);
                int G = (int) (0.349 * red + 0.586 * green + 0.068 * blue);
                int B = (int) (0.272 * red + 0.534 * green + 0.031 * blue);
                return new Color(R, G, B);
            case "Bright":
                red = Math.min(255, red + 10);
                blue = Math.min(255, blue + 10);
                green = Math.min(255, green + 10);
                return new Color(red, green, blue);
        }
        return null;
    }
  • 灰度化

    对于一个像素, 仅保留一个色彩通道的值, 使其三个色彩混合后只留下明暗信息

    常见做法为, 使用一个通道的数值代替所有通道, 或者平均其在三个通道上的数值

  • 二值化

    将整个图片的像素颜色进行二分类, 使其被转变为纯黑或者纯白的信息, 分类标准一般按照需求来制定

    常见做法为, 像素的灰度值高于一定数值将被认为是黑色, 否则为白色
    简单图像滤镜功能的Java实现_第1张图片

  • 去除背景

    不基于机器学习的简单背景去除往往也不需要进行轮廓勾勒, 可以根据一定的需求去除一定范围内的颜色

    比如, 灰度值高于一定数值可以渲染原色, 而低于一定数值会被去除

  • 负片

    负片的效果为颜色反转, 只需要使用其RGB的颜色使用最大深度减去原色数值即可


这图突然有点奇怪

  • 复古

    此类特效属于数值调整类特效, 一般做法只需要按照目前成熟的参数进行调整即可

  • 明亮

    此特效需要整体上调所有通道的数值使其更偏向于白色

    常用方法为给所有通道加上固定的值, 但注意不要超过255

对区域进行采样后实现的滤镜算法

一些算法需要对图片中的某些像素进行采样, 然后用采样并处理过的数据进行滤镜操作, 这类算法一定是有损图片质量的

    public void areaSamplingRasterize(BufferedImage bufImg, String op, boolean recOrOval, int sampleSize, int fillSize) {
        Random rand = null;
        BufferedImage orgImg = new BufferedImage(bufImg.getWidth(), bufImg.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = orgImg.getGraphics();
        g.drawImage(bufImg, 0, 0, null);
        g = bufImg.getGraphics();
        if (op == "Oil")
            rand = new Random();
        for (int i = 0; i < orgImg.getWidth(); i += sampleSize) {
            for (int j = 0; j < orgImg.getHeight(); j += sampleSize) {
                g.setColor(pixelShader(orgImg.getRGB(i, j), op));
                if (recOrOval) {
                    g.fillRect(i, j, fillSize, fillSize);
                } else {
                    int fs = fillSize;
                    if (op.equals("Oil"))
                        fs = rand.nextInt(fillSize) + sampleSize;
                    g.fillOval(i, j, fs, fs);
                }
            }
        }
        this.bufImg = bufImg;
    }
  • 油画

    油画风格的特点是着色精细度相对较粗糙, 所以可以通过对图片进行一定的随机区域着色的方法来进行制作

    常见方法为, 对一定的采样大小区域内, 填充随机大小的圆形区域, 颜色为区域内的一个采样点的颜色, 注意填充时应尽量覆盖采样区域以免留下空白(或者使用原画作为底片), 也可以进行一点点的偏色处理
    简单图像滤镜功能的Java实现_第2张图片

public Color pixelShader(int pixelVal, String op) {
    switch (op) {
        case "Mosaic":
        case "Points":
        case "Oil":
            Random rand = new Random();
            int red = (pixelVal >> 16) & 0xFF + (rand.nextInt(2) - 1);
            int green = (pixelVal >> 8) & 0xFF + (rand.nextInt(2) - 1);
            int blue = pixelVal & 0xFF + (rand.nextInt(2) - 1);
            return new Color(Math.min(255, red), Math.min(255, green), Math.min(255, blue));
    }
    return null;
}
  • 马赛克

    马赛克的简单实现就是将一个采样区域内填充成同一种采样色, 同样可以进行适当的偏色
    简单图像滤镜功能的Java实现_第3张图片

对区域处理实现的滤镜算法

图像卷积

使用一定的卷积窗口(权重矩阵) 对一个区域内的图像进行特征的提取, 综合或过滤

public void areaConvRasterize(BufferedImage bufImg, String op, int kernelSize, double[] rights) {
        BufferedImage orgImg = new BufferedImage(bufImg.getWidth(), bufImg.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = orgImg.getGraphics();
        g.drawImage(bufImg, 0, 0, null);
        for (int i = 0; i < orgImg.getWidth(); i++) {
            for (int j = 0; j < orgImg.getHeight(); j++) {
                int reds = 0, greens = 0, blues = 0;
                int edgeSize = (int) Math.sqrt(kernelSize);
                int mx = Math.min(orgImg.getWidth(), i + edgeSize);
                int my = Math.min(orgImg.getHeight(), j + edgeSize);
                ArrayList<Integer> pixelVal = new ArrayList(kernelSize);
                for (int nx = i; nx < mx; nx++) {
                    for (int ny = j; ny < my; ny++) {
                        pixelVal.add(orgImg.getRGB(nx, ny));
           
                    }
                }
                while (pixelVal.size() < kernelSize) {
                    pixelVal.add(orgImg.getRGB(i, j));
                }
                int x = Math.min(mx - 1, i + edgeSize / 2);
                int y = Math.min(my - 1, j + edgeSize / 2);
                bufImg.setRGB(x, y, convPixelShader(kernelSize, pixelVal, op, new int[]{x, y}).getRGB());
            }
        }
        this.bufImg = bufImg;
    }

常见的卷积一般以卷积窗口覆盖的中心作为目标对象, 所以对于边界来说, 处理方法一般为认为图片无限重复或者直接抛弃边界

   public Color convPixelShader(int kernelSize, ArrayList<Integer> pixelVal, String op, int[] centerPos) {
        int radius = (int) Math.sqrt(kernelSize);
        double[] rights = new double[kernelSize];
        int reds = 0, greens = 0, blues = 0;
        switch (op) {
            case "Sharpen":
                Arrays.fill(rights, 0, kernelSize, -1);
                rights[kernelSize >> 1] = 9;
                break;
            case "Blur":
                int l = 0;
                for (int x = -radius >> 1; x < radius >> 1; x++) {
                    for (int y = 0; y < radius; y++) {
                        rights[l++] = Math.pow((int) Math.E, -(x * x + y * y) / (2 * GuassBlur.sigma * GuassBlur.sigma))
                                / (2 * pi * GuassBlur.sigma * GuassBlur.sigma);
                    }
                }
                break;
        }
        for (int i = 0; i < kernelSize; i++) {
            reds += ((pixelVal.get(i) >> 16) & 0xFF) * rights[i];
            greens += ((pixelVal.get(i) >> 8) & 0xFF) * rights[i];
            blues += (pixelVal.get(i) & 0xFF) * rights[i];
        }
        reds += 20;
        greens += 20;
        blues += 20;
        reds = Math.min(255, reds);
        reds = Math.max(0, reds);
        greens = Math.min(255, greens);
        greens = Math.max(0, greens);
        blues = Math.min(255, blues);
        blues = Math.max(0, blues);
        return new Color(reds, greens, blues);
    }

  • 锐化

    锐化操作的目的是增强图片中不同颜色间边界的对比度, 突出边界

    其卷积核相对固定,常用为 3x3 卷积 的 (1,1)位置为9, 其他位置为 -1 的权重矩阵

  • 模糊

    模糊操作的实现有很多, 以高斯模糊为例, 其需要通过计算某像素相邻位置对该像素的影响来得到结果,其公式为
    在这里插入图片描述
    ​ (x,y)为卷积核的中心坐标, 一般作为坐标原点
    效果一般取决于模糊半径和σ
    简单图像滤镜功能的Java实现_第4张图片

你可能感兴趣的:(java,图像处理)