OpenCV Android简单处理

锺离明煦
2023-12-01

OpenCV Android简单处理


前言

由于项目中需要进行图片美颜相关处理,因此看了下opencv,实现还是很简单的,直接看相关内容。但存在打包出的app过大的问题,因此又参考相关实现算法,整了一套对应的bitmap处理方案。


一、Android Studio 引入opencv

app的build.gradle的引入方式如下,这里使用的是opencv 4.5.3版本:

implementation 'com.quickbirdstudios:opencv:4.5.3.0'

二、opencv初始化

使用opencv之前需要先初始化一下

/**
 * bInitOpenCV 是否已经初始化opencv
 */
public static boolean bInitOpenCV = false;

/**
 * initOpenCV 初始化opencv
 */
private static boolean initOpenCV() {
    if (bInitOpenCV)
        return true;

    boolean bStatus = OpenCVLoader.initDebug();
    if (bStatus)
        bInitOpenCV = true;

    return bInitOpenCV;
}

三、图片处理相关内容及代码

1. 图片亮白处理

opencv代码如下:

/**
 * whiteDeal opencv的图片美白显示处理
 * @param src     输入源bitmap
 * @param nFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap whiteDeal(Bitmap src, int nFactor) {
    if (!initOpenCV())
        return null;

    Mat img = new Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC4);
    Utils.bitmapToMat(src, img);

    int channels = img.channels();
    int width = img.width();
    int height = img.height();

    // 图像自然度开始处理
    byte[] bytes = new byte[channels];
    int b, g, r;
    int b_new, g_new, r_new;
    for (int row = 0; row < height; ++row)
    {
        for (int col = 0; col < width; ++col)
        {
            img.get(row, col, bytes);
            b = bytes[0] & 0xff;
            g = bytes[1] & 0xff;
            r = bytes[2] & 0xff;

            r_new = CmnUtils.getLimitValue(r + nFactor);
            g_new = CmnUtils.getLimitValue(g + nFactor);
            b_new = CmnUtils.getLimitValue(b + nFactor);

            bytes[0] = (byte)b_new;
            bytes[1] = (byte)g_new;
            bytes[2] = (byte)r_new;
            img.put(row, col, bytes);
        }
    }
    // 图像自然度结束处理

    Bitmap newValue = Bitmap.createBitmap(img.cols(), img.rows(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(img, newValue);
    return newValue;
}

bitmap代码如下:

/**
 * whiteDeal bitmap的图片美白显示处理
 * @param src       输入的bitmap源
 * @param nFactor   影响因子
 * @return bitmap   输出的bitmap
 */
public static Bitmap whiteDeal(Bitmap src, int nFactor) {
    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
    //
    int A, R, G, B;
    int pixel;

    for (int x = 0; x < width; ++x) {
        for (int y = 0; y < height; ++y) {
            pixel = src.getPixel(x, y);
            A = Color.alpha(pixel);
            R = Color.red(pixel);
            G = Color.green(pixel);
            B = Color.blue(pixel);

            // in
            R = CmnUtils.getLimitValue(R + nFactor);
            G = CmnUtils.getLimitValue(G + nFactor);
            B = CmnUtils.getLimitValue(B + nFactor);

            bmOut.setPixel(x, y, Color.argb(A, R, G, B));
        }
    }

    return bmOut;
}

2. 图片饱和度内容处理

opencv代码如下:

/**
 * VibranceAlgorithm opencv饱和度显示处理
 * @param src     输入源bitmap
 * @param nFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap VibranceAlgorithm(Bitmap src, int nFactor) {
    if (!initOpenCV())
        return null;

    Mat img = new Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC4);
    Utils.bitmapToMat(src, img);

    nFactor += 65; // +50为了初始图片

    int channels = img.channels();
    int width = img.width();
    int height = img.height();

    // 图像饱和度开始处理
    float fIncrement = (float)((nFactor - 80) * 1.0 / CmnUtils.max_Increment);
    byte[] bytes = new byte[channels];
    int b = 0, g = 0, r = 0;
    int b_new, g_new, r_new;
    for (int row = 0; row < height; ++row)
    {
        for (int col = 0; col < width; ++col)
        {
            img.get(row, col, bytes);
            b = bytes[0] & 0xff;
            g = bytes[1] & 0xff;
            r = bytes[2] & 0xff;

            // 处理
            int nMax = Math.max(Math.max(b, g), r);
            int nMin = Math.min(Math.min(b, g), r);

            float delta, value;
            float L, S, alpha;
            delta = (float)((nMax - nMin) / 255.0);
            if (delta == 0)
                continue;
            value = (float)((nMax + nMin) / 255.0);
            L = value / 2;
            if (L < 0.5)
                S = delta / value;
            else
                S = delta / (2 - value);
            if (fIncrement >= 0)
            {
                if ((fIncrement + S) >= 1)
                    alpha = S;
                else
                    alpha = 1 - fIncrement;
                alpha = 1 / alpha - 1;
                r_new = CmnUtils.getLimitValue((int)(r + (r - L * 255) * alpha));
                g_new = CmnUtils.getLimitValue((int)(g + (g - L * 255) * alpha));
                b_new = CmnUtils.getLimitValue((int)(b + (b - L * 255) * alpha));
            } else {
                alpha = fIncrement;
                r_new = CmnUtils.getLimitValue((int)(L * 255 + (r - L * 255) * (1 + alpha)));
                g_new = CmnUtils.getLimitValue((int)(L * 255 + (g - L * 255) * (1 + alpha)));
                b_new = CmnUtils.getLimitValue((int)(L * 255 + (b - L * 255) * (1 + alpha)));
            }

            bytes[0] = (byte)b_new;
            bytes[1] = (byte)g_new;
            bytes[2] = (byte)r_new;
            img.put(row, col, bytes);
        }
    }
    // 图像饱和度结束处理

    Bitmap newValue = Bitmap.createBitmap(img.cols(), img.rows(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(img, newValue);
    return newValue;
}

bitmap代码如下:

/**
 * VibranceAlgorithm bitmap的饱和度内容处理
 * @param src     输入源bitmap
 * @param nFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap VibranceAlgorithm(Bitmap src, int nFactor) {
    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());

    int pixel;
    nFactor += 100;

    // 图像饱和度开始处理
    float fIncrement = (float)((nFactor - 80) * 1.0 / CmnUtils.max_Increment);
    int a, b, g, r;
    int b_new, g_new, r_new;
    for (int y = 0; y < width; ++y) {
        for (int x = 0; x < height; ++x) {
            pixel = src.getPixel(y, x);
            a = Color.alpha(pixel);
            r = Color.red(pixel);
            g = Color.green(pixel);
            b = Color.blue(pixel);

            int nMax = Math.max(Math.max(b, g), r);
            int nMin = Math.min(Math.min(b, g), r);

            float delta, value;
            float L, S, alpha;
            delta = (float)((nMax - nMin) / 255.0);
            if (delta == 0) {
                r_new = r;
                g_new = g;
                b_new = b;
            } else {
                value = (float) ((nMax + nMin) / 255.0);
                L = value / 2;
                if (L < 0.5)
                    S = delta / value;
                else
                    S = delta / (2 - value);
                if (fIncrement >= 0) {
                    if ((fIncrement + S) >= 1)
                        alpha = S;
                    else
                        alpha = 1 - fIncrement;
                    alpha = 1 / alpha - 1;
                    r_new = CmnUtils.getLimitValue((int) (r + (r - L * 255) * alpha));
                    g_new = CmnUtils.getLimitValue((int) (g + (g - L * 255) * alpha));
                    b_new = CmnUtils.getLimitValue((int) (b + (b - L * 255) * alpha));
                } else {
                    alpha = fIncrement;
                    r_new = CmnUtils.getLimitValue((int) (L * 255 + (r - L * 255) * (1 + alpha)));
                    g_new = CmnUtils.getLimitValue((int) (L * 255 + (g - L * 255) * (1 + alpha)));
                    b_new = CmnUtils.getLimitValue((int) (L * 255 + (b - L * 255) * (1 + alpha)));
                }
            }

            bmOut.setPixel(y, x, Color.argb(a, r_new, g_new, b_new));
        }
    }
    // 图像饱和度结束处理

    return bmOut;
}

3. 图片自然显示处理

opencv代码如下:

/**
 * contrastBright opencv的图片自然显示处理
 * @param src     输入源bitmap
 * @param nFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap contrastBright(Bitmap src, int nFactor) {
    if (!initOpenCV())
        return null;

    Mat img = new Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC4);
    Utils.bitmapToMat(src, img);

    nFactor += 18; // +18是为了图像和初始图片比较相似

    int channels = img.channels();
    int width = img.width();
    int height = img.height();

    // 图像自然度开始处理
    // 对比度 = 亮度 / 1.5;
    int contrastValue = nFactor / 2;
    float fContrastValue = (float)(0.1 * contrastValue);

	// 优化性能处理
	int arrColorValue[] = new int[256];
	for (int i = 0; i < 256; ++i) {
		arrColorValue[i] = CmnUtils.getLimitValue((int)(fContrastValue * r + nFactor));
	}

    byte[] bytes = new byte[channels];
    int b = 0, g = 0, r = 0;
    int b_new, g_new, r_new;
    for (int row = 0; row < height; ++row)
    {
        for (int col = 0; col < width; ++col)
        {
            img.get(row, col, bytes);
            b = bytes[0] & 0xff;
            g = bytes[1] & 0xff;
            r = bytes[2] & 0xff;

            r_new = arrColorValue[r];
            g_new = arrColorValue[g];
            b_new = arrColorValue[b];

            bytes[0] = (byte)b_new;
            bytes[1] = (byte)g_new;
            bytes[2] = (byte)r_new;
            img.put(row, col, bytes);
        }
    }
    // 图像自然度结束处理

    Bitmap newValue = Bitmap.createBitmap(img.cols(), img.rows(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(img, newValue);
    return newValue;
}

bitmap代码如下:

/**
 * contrastBright bitmap的图片自然显示处理
 * @param src     输入源bitmap
 * @param nFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap contrastBright(Bitmap src, int nFactor) {
    nFactor += 18; // +18是为了图像和初始图片比较相似

    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());

    // 图像自然度开始处理
    // 对比度 = 亮度 / 1.5;
    int contrastValue = nFactor / 2;
    float fContrastValue = (float)(0.1 * contrastValue);

	// 优化性能处理
	int arrColorValue[] = new int[256];
	for (int i = 0; i < 256; ++i) {
		arrColorValue[i] = CmnUtils.getLimitValue((int)(fContrastValue * r + nFactor));
	}

    int pixel;
    int a, b, g, r;
    int b_new, g_new, r_new;
    for (int x = 0; x < height; ++x) {
        for (int y = 0; y < width; ++y) {
            pixel = src.getPixel(y, x);
            a = Color.alpha(pixel);
            r = Color.red(pixel) & 0xFF;
            g = Color.green(pixel) & 0xFF;
            b = Color.blue(pixel) & 0xFF;

            r_new = arrColorValue[r];
            g_new = arrColorValue[g];
            b_new = arrColorValue[b];

            bmOut.setPixel(y, x, Color.argb(a, r_new, g_new, b_new));
        }
    }
    // 图像自然度结束处理

    return bmOut;
}

4. 图片清晰处理

opencv代码如下:

/**
 * usmDeal opencv图片清晰显示处理
 * @param src     输入源bitmap
 * @param fFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap usmDeal(Bitmap src, float fFactor) {
    if (!initOpenCV())
        return null;

    Mat img = new Mat(src.getHeight(), src.getWidth(), CvType.CV_8UC4);
    Utils.bitmapToMat(src, img);

    // 图像清晰度开始处理
    Mat blurMask = new Mat();
    // 高斯模糊
    Imgproc.GaussianBlur(img, blurMask, new Size(3, 3), 3, 3);
    // 图片叠加
    Core.addWeighted(img, 1 + fFactor, blurMask, -fFactor, 0, img);
    // 图像清晰度结束处理

    Bitmap newValue = Bitmap.createBitmap(img.cols(), img.rows(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(img, newValue);
    return newValue;
}

bitmap代码如下:

/**
 * usmDeal bitmap的图片清晰显示处理
 * @param context 上下文
 * @param src     输入源bitmap
 * @param fFactor 影响因子
 * @return bitmap 返回对应的bitmap结果
 */
public static Bitmap usmDeal(Context context, Bitmap src, float fFactor) {
    // 图像清晰度开始处理
    // 高斯模糊
    Bitmap bitBlur = CmnUtils.gaussBlur(context, src, 1.1f);
    return CmnUtils.addWeighted(src, 1 + fFactor, bitBlur, -fFactor, 0);
    // 图像清晰度结束处理;
}

5. CmnUtils代码如下

/**
 * CmnUtils 图片处理通用处理方法
 */
public class CmnUtils {
    //
    public final static int max_Increment = 200;

    /**
     * getLimitValue 获取指定范围内的值
     * @param value 传入值
     * @return 对应的限定值
     */
    public static int getLimitValue(int value) {
        if (value > 255)
            value = 255;
        else if (value < 0)
            value = 0;
        return value;
    }

    /**
     * gaussBlur 高斯模糊处理
     * @param context   上下文
     * @param bitmap    输入的bitmap
     * @param radius    对应的高斯模糊半径
     */
    public static Bitmap gaussBlur(Context context, Bitmap bitmap, float radius) {
        // 创建输出图片
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        // 创建输出图片
        RenderScript rs = RenderScript.create(context);
        // 创建高斯模糊脚本
        ScriptIntrinsicBlur gaussianBlur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        // 创建用于输入的脚本类型
        Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
        // 创建用于输出的脚本类型
        Allocation allOut = Allocation.createFromBitmap(rs, output);
        // 设置模糊半径,范围0f<radius<=25f
        gaussianBlur.setRadius(radius);
        // 设置输入脚本类型
        gaussianBlur.setInput(allIn);
        // 执行高斯模糊算法,并将结果填入输出脚本类型中
        gaussianBlur.forEach(allOut);
        // 将输出内存编码为Bitmap,图片大小必须注意
        allOut.copyTo(output);
        // 关闭RenderScript对象,API>=23则使用rs.releaseAllContexts()
        rs.destroy();
        return output;
    }

    /**
     * addWeighted 把两张相同尺寸的图片合并到一起
     * @param first  输入图片1
     * @param alpha  图片1的融合比例
     * @param second 输入图片2
     * @param beta   图片2的融合比例
     * @param gamma  偏差
     * @return bitmap 返回对应的bitmap结果
     */
    public static Bitmap addWeighted(Bitmap first, float alpha, Bitmap second, float beta, double gamma) {
        int width = first.getWidth();
        int height = first.getHeight();
        Bitmap bmOut = Bitmap.createBitmap(width, height, first.getConfig());
        if (width != second.getWidth() || height != second.getHeight())
            return bmOut;

        int pixel1, pixel2;
        int a, b1, g1, r1;
        int b2, g2, r2;
        int b_new, g_new, r_new;
        for (int x = 0; x < height; ++x) {
            for (int y = 0; y < width; ++y) {
                pixel1 = first.getPixel(y, x);
                a = Color.alpha(pixel1);
                r1 = Color.red(pixel1);
                g1 = Color.green(pixel1);
                b1 = Color.blue(pixel1);

                pixel2 = second.getPixel(y, x);
                r2 = Color.red(pixel2);
                g2 = Color.green(pixel2);
                b2 = Color.blue(pixel2);

                b_new = CmnUtils.getLimitValue((int)(b1 * alpha + b2 * beta + gamma));
                g_new = CmnUtils.getLimitValue((int)(g1 * alpha + g2 * beta + gamma));
                r_new = CmnUtils.getLimitValue((int)(r1 * alpha + r2 * beta + gamma));

                bmOut.setPixel(y, x, Color.argb(a, r_new, g_new, b_new));
            }
        }

        return bmOut;
    }
}

总结

opencv使用时,需要先初始化一下。以上代码已经很详细了,目前只是简单的使用,后面涉及人脸识别之类的,就需要导入对应的人脸识别库文件。目前存在的问题主要有两个:

  1. 处理的效果差强人意
  2. 处理效率非常差
    但就测试而言,opencv的效率普遍没有bitmap的处理速度快,应该是opencv没有使用硬件加速(GPU)导致的。

最后附上源码,有需要的可以自己看下


 类似资料: