package com.example.opencvapplication;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class Expt_7 extends AppCompatActivity {
private Bitmap select_bitmap;
private double max_size = 1024;
private ImageView img_expt_7;
private CascadeClassifier faceDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expt7);
//初始化加载OpenCV
OpenCVLoader.initDebug();
Button button_go_home7 = findViewById(R.id.go_home7);
//添加按钮点击响应
button_go_home7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//界面跳转
startActivity(new Intent(Expt_7.this,MainActivity.class));
}
});//
Button btn_imgLoad_7 = findViewById(R.id.btn_imgLoad_7);
Button btn_faceBeauty = findViewById(R.id.btn_faceBeauty);
Button btn_restore_7 = findViewById(R.id.btn_restore_7);
img_expt_7 = findViewById(R.id.img_expt_7);
btn_imgLoad_7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 打开文件选择器
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
//指定只显示图片
intent.setType("image/*");
// 实现跳转
fromAlbumDataLauncher.launch(intent);
}
});
btn_restore_7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Bitmap bitmap = select_bitmap.copy(select_bitmap.getConfig(), true);
img_expt_7.setImageBitmap(bitmap);
}
});
btn_faceBeauty.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//构建Bitmap类型对象,拷贝载入的图片
Bitmap bitmap = select_bitmap.copy(select_bitmap.getConfig(), true);
//原图像RGBA类型
Mat src = new Mat();
Utils.bitmapToMat(bitmap, src);
Imgproc.cvtColor(src, src, Imgproc.COLOR_RGBA2RGB);
//目标图像,美颜后的图像
Mat dst = new Mat(src.size(),src.type());
//高斯双边滤波
Imgproc.bilateralFilter(src,dst,0,150,10);
//生成遮罩层
Mat mask = generateMask(src);
//对mask实现高斯模糊
Imgproc.cvtColor(mask,mask,Imgproc.COLOR_RGBA2GRAY);
Imgproc.GaussianBlur(mask, mask,new Size( 3, 3),0,0,
Core.BORDER_DEFAULT);
//mask模糊图像转换类型,归一化
Mat blur_mask =new Mat();
mask.convertTo(blur_mask,CvType.CV_32F);
Core.normalize(blur_mask,blur_mask, 1.0,0,Core.NORM_MINMAX);
//根据归一化后的模版完成高斯融合
GaussianAdd(src,dst,blur_mask);
// 边缘提升
Mat border =new Mat();
Mat temp_mat = new Mat();
//获取图像上的边缘(此时图像上的边缘为白色)
Imgproc.Canny(src,border,50,150,3,true);
//将图像边缘的色彩通过与计算賦值给temp_mat
Core.bitwise_and(dst,dst,temp_mat,border);
Core.addWeighted(dst,0.9,temp_mat,0.1,0, dst);
//亮度提升
Core.add(dst,new Scalar(40,40,40),dst);
Utils.matToBitmap(dst,bitmap);
img_expt_7.setImageBitmap(bitmap);
//释放内存
src.release();
dst.release();
mask.release();
blur_mask.release();
border.release();
temp_mat.release();
}
});
}
//注册对Activity结果的监听
//新建一个ActivityResultLauncher对象,使用该对象的launch方法来实现跳转
//并且另一个活动结束时会回调该对象registerForActivityResult()方法,我们在回调的时获取并执行数据
private ActivityResultLauncher fromAlbumDataLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
//Uri代表要操作的数据,Android上可用的资源(图像、视频片段)
Uri uri = result.getData().getData();
if (uri != null) {
//将选择的图片显示
try {
//读取图片并进行降采样
select_bitmap = getBitmapFromUri(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
if (select_bitmap != null) {
//图像关联实现窗口
img_expt_7.setImageBitmap(select_bitmap);
}
}
}
});
private Bitmap getBitmapFromUri(Uri uri) throws FileNotFoundException {
Bitmap bitmap_temp = null;
//使用ContentResolver通过uri打开输入流
InputStream input = getContentResolver().openInputStream(uri);
// options.inJustDecodeBounds = true;
// 这时候decode的bitmap为null,只是把图片的高度放在Option里
// 避免大图片溢出问题
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(input, null, options);
//---------图像将采样,限制载入图像的大小-------
int raw_width = options.outWidth;
int raw_height = options.outHeight;
Log.i("Image-tag", "Image size:" + raw_height + " " + raw_width);
int max = Math.max(raw_height, raw_width);
int new_width = raw_width;
int new_height = raw_height;
int inSampleSize = 1;
if (max > max_size) {
new_width = raw_width;
new_height = raw_height;
//设置合适的压缩比例inSampleSize
while ((new_width / inSampleSize) > max_size || (new_height / inSampleSize) > max_size) {
inSampleSize *= 2;
}
}
//设置合适的缩放比例,将options。inJustDecodeBounds = false
//重新读出图片
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
// 表示每个通道占8位,总计4个字节
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap_temp = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
Log.i("Image-tag-1", "Image size: " + bitmap_temp.getHeight() + " " + bitmap_temp.getWidth());
return bitmap_temp;
}
private Mat generateMask(Mat src) {
//获取图像的通道数,宽度,高度,用于遍历图像像素
int channels = src.channels();
int width = src.cols();
int height = src.rows();
int B = 0, G = 0, R = 0;
byte[] data = new byte[width * height * channels];
//将src中的数据存入data
src.get(0, 0, data);
//遍历data中的所有元素
for (int i = 0; i < data.length; i++) {
if (0 == i % channels) {
//获取像素的RGB三通道数
R = data[i] & 0xff;
G = data[i + 1] & 0xff;
B = data[i + 2] & 0xff;
//判断是否是皮肤区域
if (R > 95 && 6 > 40 && B > 20 && (Math.max(R, Math.max(G, B)) - Math.min(R, Math.min(R, B))) > 15
&& Math.abs(R - G) > 15 && R > G && R > B) {
data[i] = (byte) 255;
data[i + 1] = (byte) 255;
data[i + 2] = (byte) 255;
}
}
}
Mat dst = new Mat(src.size(), src.type());
dst.put(0, 0, data);
return dst;
}
private void GaussianAdd(Mat src,Mat dst,Mat blur_mask){
int width = src.cols();
int height = src.rows();
int channels = src.channels();
byte[] data_1 = new byte[width*height*channels];
byte[] data_2 = new byte[width*height*channels];
float[] mask_data = new float[width*height];
blur_mask.get(0,0,mask_data);
src.get(0,0,data_1);
dst.get(0,0,data_2);
for(int row = 0;row<height;row++){
for(int col = 0;col<width;col++){
int r1 = data_1[row*channels*width+col*channels]&0xff;
int g1 = data_1[row*channels*width+col*channels+1]&0xff;
int b1 = data_1[row*channels*width+col*channels+2]&0xff;
int r2 = data_2[row*channels*width+col*channels]&0xff;
int g2 = data_2[row*channels*width+col*channels+1]&0xff;
int b2 = data_2[row*channels*width+col*channels+2]&0xff;
float w2 = mask_data[row*width+col];
float w1 = 1.0f-w2;
r2 = (int) (r2*w2+w1*r1);
g2 = (int) (g2*w2+w1*g1);
b2 = (int) (b2*w2+w1*b1);
data_2[row*channels*width+col*channels] = (byte) r2;
data_2[row*channels*width+col*channels+1] = (byte) g2;
data_2[row*channels*width+col*channels+2] = (byte) b2;
}
}
dst.put(0,0,data_2);
}
}
人脸美颜等功能
最新推荐文章于 2025-05-23 15:55:42 发布