由于项目中需要进行图片美颜相关处理,因此看了下opencv,实现还是很简单的,直接看相关内容。但存在打包出的app过大的问题,因此又参考相关实现算法,整了一套对应的bitmap处理方案。
app的build.gradle的引入方式如下,这里使用的是opencv 4.5.3版本:
implementation 'com.quickbirdstudios:opencv:4.5.3.0'
使用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;
}
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;
}
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;
}
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;
}
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);
// 图像清晰度结束处理;
}
/**
* 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使用时,需要先初始化一下。以上代码已经很详细了,目前只是简单的使用,后面涉及人脸识别之类的,就需要导入对应的人脸识别库文件。目前存在的问题主要有两个:
最后附上源码,有需要的可以自己看下