Fast edge retention filtering

Fast edge retention filter is an edge retention fuzzy algorithm which realizes local mean square error by integrating image. Firstly, the formula for calculating the local mean value in the local mean square filtering is as follows:

Calculate the local mean.png

When the edge is very weak, the coefficient K tends to 0, and the pixel value of this point after correction is close to the average value. When the edge is very strong, the coefficient K approaches 1, and the pixel value after the blur of this point is close to the input pixel value. In the above calculation, the most favorite is the mean and variance of pixels in the window. The calculated mean can be easily obtained according to the integral image, while the calculated variance can be obtained according to a series of mathematical derivation as follows

Results. PNG

The steps of algorithm realization are as follows:

1. Fast edge retention filtering

The core algorithm is as follows:

    @Override
    public ImageProcessor filter(ImageProcessor src) {
        // initialization parameters
        int width = src.getWidth();
        int height = src.getHeight();
        xr = yr = (int)(Math.max(width, height) * 0.02);
        sigma = 10 + sigma * sigma * 5;

        // start ep process
        byte[] output = new byte[width*height];
        IntIntegralImage ii = new IntIntegralImage();
        for(int i=0; i<src.getChannels(); i++) {
            System.arraycopy(src.toByte(i), 0, output, 0, output.length);
            ii.setImage(src.toByte(i));
            ii.process(width, height, true);
            processSingleChannel(width, height, ii, output);
            System.arraycopy(output, 0, src.toByte(i), 0, output.length);
        }

        // release memory
        output = null;
        return src;
    }

    public void processSingleChannel(int width, int height, IntIntegralImage input, byte[] output) {
        float sigma2 = sigma*sigma;
        int offset = 0;
        int wy = (yr * 2 + 1);
        int wx = (xr * 2 + 1);
        int size = wx * wy;
        int r = 0;
        for (int row = yr; row < height-yr; row++) {
            offset = row * width;
            for (int col = xr; col < width-xr; col++) {
                int sr = input.getBlockSum(col, row, wy, wx);
                float a = input.getBlockSquareSum(col, row, wy, wx);
                float b = sr / size;
                float c = (a - (sr*sr)/size)/size;
                float d = c / (c+sigma2);
                r = (int) ((1-d)*b + d*r);
                output[offset + col] = (byte)Tools.clamp(r); }}}Copy the code

Among them, IntIntegralImage encapsulates the algorithm of integral image, which can be seen in cv4j.

2. Skin test

Skin detection was realized based on simple threshold skin color recognition of RGB color space, And the algorithm was as follows: R>95 And G>40 And B>20 And R>G And R>B And Max(R,G,B) -min (R,G,B)>15 And Abs(R-G)>15

public class DefaultSkinDetection implements ISkinDetection{
// RGB Color model pixel skin detection method
// (R, G, B) is classified as skin if:
// R > 95 and G > 40 and B > 20 and
// max(R, G, B) - min(R, G, B) > 15 and
// |R-G| > 15 and R > G and R > B
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    @Override
    public boolean findSkin(int tr, int tg, int tb) {
        return isSkin(tr, tg, tb);
    }

    @Override
    public boolean isSkin(int tr, int tg, int tb) {
        int max = Math.max(tr, Math.max(tg, tb));
        int min = Math.min(tr, Math.min(tg, tb));
        int rg = Math.abs(tr - tg);
        if(tr > 95 && tg > 40 && tb > 20 && rg > 15 && 
                (max - min) > 15 && tr > tg && tr > tb) {
            return true;
        } else {
            return false; }}}Copy the code

3. Gradient filtering

Gradient filter is also called high pass filter. There are several different ways of using gradient filters, and in this case Sobel. Sobel operator detects the edge according to the phenomenon that the gray weighted difference between the upper and lower and the left and right adjacent points of the pixel reaches the extreme value at the edge. It can smooth the noise and provide more accurate edge direction information, but the edge positioning accuracy is not high enough. It is a common edge detection method when the precision is not very high. The Sobel algorithm is chosen here because it is simple and efficient.

4. BeautySkinFilter

Combined with the above three steps, in CV4J to achieve face skin filter BeautySkinFilter

package com.cv4j.core.filters;

import com.cv4j.core.datamodel.ByteProcessor;
import com.cv4j.core.datamodel.ImageProcessor;

/** * Created by gloomy fish on 2017/4/23. */

public class BeautySkinFilter implements CommonFilter {
    @Override
    public ImageProcessor filter(ImageProcessor src) {
        int width = src.getWidth();
        int height = src.getHeight();
        byte[] R = new byte[width*height];
        byte[] G = new byte[width*height];
        byte[] B = new byte[width*height];
        System.arraycopy(src.toByte(0), 0, R, 0, R.length);
        System.arraycopy(src.toByte(1), 0, G, 0, G.length);
        System.arraycopy(src.toByte(2), 0, B, 0, B.length);

        FastEPFilter epFilter = new FastEPFilter();
        epFilter.filter(src);
        ISkinDetection skinDetector = new DefaultSkinDetection();
        int r = 0, g = 0, b = 0;
        for(int i=0; i<R.length; i++) {
            r = R[i]&0xff;
            g = G[i]&0xff;
            b = B[i]&0xff;
            if(! skinDetector.isSkin(r, g, b)) { src.toByte(0)[i] = (byte)r;
                src.toByte(1)[i] = (byte)g;
                src.toByte(2)[i] = (byte)b; }}byte[] gray = new byte[width*height];
        int c = 0;
        for(int i=0; i<R.length; i++) {
            r = R[i] & 0xff;
            g = G[i] & 0xff;
            b = B[i] & 0xff;
            c = (int) (0.299 *r + 0.587*g + 0.114*b);
            gray[i] = (byte)c;
        }

        GradientFilter gradientFilter = new GradientFilter();
        int[] gradient = gradientFilter.gradient(new ByteProcessor(gray, width, height));
        gray = null;
        for(int i=0; i<R.length; i++) {
            r = R[i]&0xff;
            g = G[i]&0xff;
            b = B[i]&0xff;
            if(gradient[i] > 50) {
                src.toByte(0)[i] = (byte)r;
                src.toByte(1)[i] = (byte)g;
                src.toByte(2)[i] = (byte)b; }}returnsrc; }}Copy the code

5. End result

The BeautySkinFilter is used the same way as the original filter, one line of code to achieve the desired effect 🙂

RxImageData.bitmap(bitmap).addFilter(new BeautySkinFilter()).into(image1);Copy the code

Take a look at the end result on Android:

Face skin effect. PNG

Conclusion:

Cv4j is an image processing library hyperyfish and I developed together, with a pure Java implementation, which is still in its early version. There is still room for improvement in the face skin grinding algorithm, and we will continue to optimize the algorithm in the future.

It’s a shame that we didn’t have time to complete the development documentation due to our busy schedule. During the upcoming May Day holiday, we will update the documentation and make more cool features in the future.

Previous articles: binary image analysis — case combat (text separation + coin counting) Java to achieve gaussian blur and image space convolution Java to achieve image filter advanced gameplay Java to achieve image filter effect

Mining technology: juejin.cn/post/684490…