OpenGL ES 结合 OpenCV,图像处理(滤镜、人脸识别)

GLCVActivity

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

import java.nio.ByteBuffer;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import hankin.hjmedia.R;
import hankin.hjmedia.logger.LogUtils;
import hankin.hjmedia.opencv.OpenCVBaseActivity;
import hankin.hjmedia.opencv.utils.CV310Utils;
import hankin.hjmedia.utils.SDCardStoragePath;
import hankin.hjmedia.utils.WindowUtils;

public class GLCVActivity extends OpenCVBaseActivity {

    public static void startGLCVActivity(Activity activity) {
        activity.startActivity(new Intent(activity, GLCVActivity.class));
    }

    @BindView(R.id.glsv_glcv) GLSurfaceView mGLSurfaceView;
    private GLCVRender mGLCVRender;
    @BindView(R.id.iv_glcv_frame) ImageView mFrameIv;
    private long mStartTime = System.currentTimeMillis();
    private Mat mRGBAMat = new Mat(); // c 中设置数据

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        setScreenOrientation(false);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            setTheme(R.style.style_videorecord);
        }
        super.onCreate(savedInstanceState);
        WindowUtils.toggleStatusAndNavigationBar(this);
        setContentView(R.layout.activity_glcv);
        mUnbinder = ButterKnife.bind(this);

        mGLCVRender = new GLCVRender(this, mGLSurfaceView, new GLCVRender.OnFrameOutListener() {
            @Override
            public void rgbaOut(final int width, final int height, ByteBuffer buffer) { // rgba out 从gl中截图
                mStartTime = System.currentTimeMillis();
                final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);
                buffer.clear();
                LogUtils.d("mydebug---", "rgbaOut time="+(System.currentTimeMillis()-mStartTime)); // rgbaOut time=25 比较耗时
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mStartTime = System.currentTimeMillis();
                        mFrameIv.setImageBitmap(mirrorBitmap(bitmap));
                        mFrameIv.setVisibility(View.VISIBLE);
                        LogUtils.d("mydebug---", "postScale time="+(System.currentTimeMillis()-mStartTime)); // postScale time=41 比较耗时
                    }
                });
            }

            @Override
            public void yuvOut(final int width, final int height, final byte[] data) { // yuv out 从gl中截图
                runOnUiThread(new Runnable() {//直接显示,不保存到本地
                    @Override
                    public void run() {
                        mStartTime = System.currentTimeMillis();
                        Bitmap bitmap = rawByteArray2RGBABitmap2_nv21(data, width, height);
                        mFrameIv.setImageBitmap(bitmap);
                        mFrameIv.setVisibility(View.VISIBLE);
                        LogUtils.d("mydebug---", "yuv2rgba & mirror time="+(System.currentTimeMillis()-mStartTime)); // postScale time=41 比较耗时
                    }
                });
            }
        });

        // opencv/etc/lbpcascades/lbpcascade_frontalface_improved.xml 检测出人脸是小脸框
        // opencv/etc/haarcascades/haarcascade_frontalface_alt.xml 检测结果比 lbp 的准确些,两者耗时差不多。。。
        String cascadesPath = CV310Utils.asset2SDcard(this, "opencv/etc/lbpcascades/lbpcascade_frontalface_improved.xml",
                SDCardStoragePath.DEFAULT_OPENCV_CASCADE_LIBPC, "lbpcascade_frontalface_improved.xml");// 训练好了的人脸数据,从assets转存到sdcard
        init(cascadesPath, mGLCVRender.mPreviewLongSide, mGLCVRender.mPreviewShortSide);
    }

    // jni中调用,将 c mat 传递给 java mat,然后显示
    public void cmat2jmat() {
        if (!mRGBAMat.empty()) {
            Bitmap bmp = Bitmap.createBitmap(mRGBAMat.cols(), mRGBAMat.rows(), Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(mRGBAMat, bmp);
            mFrameIv.setImageBitmap(bmp);
            mFrameIv.setVisibility(View.VISIBLE);
            // cframe time : 42, 1280, 720    速度很快,比java的要慢一些。加上人脸检测系列代码后 : cframe time : 299, 720, 1280
            LogUtils.d("mydebug---", "cframe time : " + (System.currentTimeMillis() - mStartTime) + ", " + mRGBAMat.cols() + ", " + mRGBAMat.rows());
        } else {
            LogUtils.d("mydebug---", "mRGBAMat empty");
        }
    }

    // 上下镜像 bitmap
    private Bitmap mirrorBitmap(Bitmap bitmap) {
        int height = bitmap.getHeight();
        int width = bitmap.getWidth();
        Matrix matrix = new Matrix();
        matrix.postScale(1, -1);//上下镜像
        Bitmap bt = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);//利用矩阵创建新的Bitmap
        if (!bitmap.isRecycled()) {
            bitmap.recycle();
        }
        return bt;
    }

    //YCbCr_420_SP (NV21) 转换成 ARGB
    private Bitmap rawByteArray2RGBABitmap2_nv21(byte[] data, int width, int height) {
        int frameSize = width * height;
        int[] rgba = new int[frameSize];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int y = (0xff & ((int) data[i * width + j]));
                int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
                int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
                y = y < 16 ? 16 : y;
                int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
                int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
                int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
                r = r < 0 ? 0 : (r > 255 ? 255 : r);
                g = g < 0 ? 0 : (g > 255 ? 255 : g);
                b = b < 0 ? 0 : (b > 255 ? 255 : b);
                rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
            }
        }
        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        bmp.setPixels(rgba, 0 , width, 0, 0, width, height);
        return bmp;
    }

    @OnClick({R.id.btn_glcv_switch, R.id.btn_glcv_mirror, R.id.btn_glcv_gray, R.id.iv_glcv_frame, R.id.btn_glcv_javaframe,
            R.id.btn_glcv_cframe,  R.id.btn_glcv_face, R.id.btn_glcv_rgbaout, R.id.btn_glcv_yuvout})
    void click(View view) {
        int w = mGLCVRender.mPreviewLongSide, h = mGLCVRender.mPreviewShortSide;
        int len = (int) (w * h * 1.5);
        switch (view.getId()) {
            case R.id.btn_glcv_switch: // 切换前后摄像头
                mGLCVRender.switchCamera();
                break;
            case R.id.btn_glcv_mirror: // 左右镜像
                mGLCVRender.isMirror = !mGLCVRender.isMirror;
                ((Button)findViewById(R.id.btn_glcv_mirror)).setText("mirror "+mGLCVRender.isMirror);
                break;
            case R.id.btn_glcv_gray: // 是否灰度filter
                mGLCVRender.isGrayFilter = !mGLCVRender.isGrayFilter;
                ((Button)findViewById(R.id.btn_glcv_gray)).setText("gray "+mGLCVRender.isGrayFilter);
                break;
            case R.id.iv_glcv_frame:
                mFrameIv.setVisibility(View.GONE);
                break;
            case R.id.btn_glcv_javaframe: // java中使用OpneCV的Mat,捕获camera的帧数据,显示

                mStartTime = System.currentTimeMillis();
                byte[] j_frame = new byte[len];
                System.arraycopy(mGLCVRender.mCameraFrameBuffer, 0, j_frame, 0, len); // 从 mGLCVRender.mCameraFrameBuffer 拷贝 len 个字节到 j_frame 中
                // camera默认导出的是nv21格式的数据,需要转换为rbga, w 比 h 大,反了的话,图像不正确
                Mat j_nv21 = new Mat(h + h/2, w, CvType.CV_8UC1); // camera预览,不管手机竖着拿还是横着拿,都是width大于height
                j_nv21.put(0, 0, j_frame); // 从 0,0 位置开始,将j_frame中的数据放到Mat中,j_frame中颜色数据排列顺序为 rgba
                Mat j_rgba = new Mat(); // 这里不用设置mat的大小、类型,下面的颜色空间转换会帮助设置好
                Imgproc.cvtColor(j_nv21, j_rgba, Imgproc.COLOR_YUV2RGB_NV21, 4); // nv21转rbga,最后一个参数表示rgba的4个通道数
                Bitmap j_bmp = Bitmap.createBitmap(j_rgba.cols(), j_rgba.rows(), Bitmap.Config.ARGB_8888);
                Utils.matToBitmap(j_rgba, j_bmp);
                mFrameIv.setImageBitmap(j_bmp);
                mFrameIv.setVisibility(View.VISIBLE);
                // javaframe time : 20, 1280, 720    速度很快
                LogUtils.d("mydebug---", "javaframe time : "+(System.currentTimeMillis()-mStartTime)+", "+j_rgba.cols()+", "+j_rgba.rows());
                j_nv21.release(); // 释放 Mat
                j_rgba.release();

                break;
            case R.id.btn_glcv_cframe:

                // opencv 中 java 中的 Mat 与 c++ 中的 Mat 互转
                /*
                    c++中的Mat传递给java中的Mat:
                        1、可以在java部分创建一个Mat,用于保存图像处理结果图像,获取Mat 的本地地址传入jni函数中:
                            Mat res = new Mat(); // java 中new一个空的mat
                            jni_fun(res.getNativeObjAddr()); // jni 函数, 将mat的地址,即 public final long nativeObj 成员变量 传递给ndk
                        2、c++部分新建Mat指针指向java传入的内存区域,将处理后的结果图像的Mat数据复制到这块内存区域,这样java中的创建的Mat就变为结果图像:
                            void jni_fun(jlong Mataddr) //jni 函数
                            {
                                Mat* res = (Mat*)MatAddr; // 定义一个指针,指向java虚拟机中的Mat的地址
                                Mat image = ndk中创建的mat; // c++中创建Mat
                                res->create(image.rows, image.cols, image.type()); // 将java中的Mat的宽高、类型设置成c++中一样
                                memcpy(res->data, image.data, image.rows*image.step); //
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值