美文网首页
图片ncnn、opencv、seeta、android之间互转

图片ncnn、opencv、seeta、android之间互转

作者: 一个摸鱼AI喵 | 来源:发表于2021-10-24 10:09 被阅读0次

    一、ncnn与opencv互换

    1.1 opencv to ncnn

    • cv::Mat CV_8UC3 -> ncnn::Mat 3 channel + swap RGB/BGR
    // cv::Mat a(h, w, CV_8UC3);
    ncnn::Mat in = ncnn::Mat::from_pixels(a.data, ncnn::Mat::PIXEL_BGR2RGB, a.cols, a.rows);
    
    • cv::Mat CV_8UC3 -> ncnn::Mat 3 channel + keep RGB/BGR order
    // cv::Mat a(h, w, CV_8UC3);
    ncnn::Mat in = ncnn::Mat::from_pixels(a.data, ncnn::Mat::PIXEL_RGB, a.cols, a.rows);
    
    • cv::Mat CV_8UC3 -> ncnn::Mat 1 channel + do RGB2GRAY/BGR2GRAY
    // cv::Mat rgb(h, w, CV_8UC3);
    ncnn::Mat inrgb = ncnn::Mat::from_pixels(rgb.data, ncnn::Mat::PIXEL_RGB2GRAY, rgb.cols, rgb.rows);
    
    // cv::Mat bgr(h, w, CV_8UC3);
    ncnn::Mat inbgr = ncnn::Mat::from_pixels(bgr.data, ncnn::Mat::PIXEL_BGR2GRAY, bgr.cols, bgr.rows);
    
    • cv::Mat CV_8UC1 -> ncnn::Mat 1 channel
    // cv::Mat a(h, w, CV_8UC1);
    ncnn::Mat in = ncnn::Mat::from_pixels(a.data, ncnn::Mat::PIXEL_GRAY, a.cols, a.rows);
    
    • cv::Mat CV_32FC1 -> ncnn::Mat 1 channel
      • You could construct ncnn::Mat and fill data into it directly to avoid data copy
    // cv::Mat a(h, w, CV_32FC1);
    ncnn::Mat in(a.cols, a.rows, 1, (void*)a.data);
    in = in.clone();
    
    • cv::Mat CV_32FC3 -> ncnn::Mat 3 channel
      • You could construct ncnn::Mat and fill data into it directly to avoid data copy
    // cv::Mat a(h, w, CV_32FC3);
    ncnn::Mat in_pack3(a.cols, a.rows, 1, (void*)a.data, (size_t)4u * 3, 3);
    ncnn::Mat in;
    ncnn::convert_packing(in_pack3, in, 1);
    
    • std::vector < cv::Mat > + CV_32FC1 -> ncnn::Mat multiple channels
      • You could construct ncnn::Mat and fill data into it directly to avoid data copy
    // std::vector<cv::Mat> a(channels, cv::Mat(h, w, CV_32FC1));
    int channels = a.size();
    ncnn::Mat in(a[0].cols, a[0].rows, channels);
    for (int p=0; p<in.c; p++)
    {
        memcpy(in.channel(p), (const uchar*)a[p].data, in.w * in.h * sizeof(float));
    }
    

    1.2 ncnn to opencv

    • ncnn::Mat 3 channel -> cv::Mat CV_8UC3 + swap RGB/BGR
      • You may need to call in.substract_mean_normalize() first to scale values from 0..1 to 0..255
    // ncnn::Mat in(w, h, 3);
    cv::Mat a(in.h, in.w, CV_8UC3);
    in.to_pixels(a.data, ncnn::Mat::PIXEL_BGR2RGB);
    
    • ncnn::Mat 3 channel -> cv::Mat CV_8UC3 + keep RGB/BGR order
      • You may need to call in.substract_mean_normalize() first to scale values from 0..1 to 0..255
    // ncnn::Mat in(w, h, 3);
    cv::Mat a(in.h, in.w, CV_8UC3);
    in.to_pixels(a.data, ncnn::Mat::PIXEL_RGB);
    
    • ncnn::Mat 1 channel -> cv::Mat CV_8UC1
      • You may need to call in.substract_mean_normalize() first to scale values from 0..1 to 0..255
    // ncnn::Mat in(w, h, 1);
    cv::Mat a(in.h, in.w, CV_8UC1);
    in.to_pixels(a.data, ncnn::Mat::PIXEL_GRAY);
    
    • ncnn::Mat 1 channel -> cv::Mat CV_32FC1
      • You could consume or manipulate ncnn::Mat data directly to avoid data copy
    // ncnn::Mat in;
    cv::Mat a(in.h, in.w, CV_32FC1);
    memcpy((uchar*)a.data, in.data, in.w * in.h * sizeof(float));
    
    • ncnn::Mat 3 channel -> cv::Mat CV_32FC3
      • You could consume or manipulate ncnn::Mat data directly to avoid data copy
    // ncnn::Mat in(w, h, 3);
    ncnn::Mat in_pack3;
    ncnn::convert_packing(in, in_pack3, 3);
    cv::Mat a(in.h, in.w, CV_32FC3);
    memcpy((uchar*)a.data, in_pack3.data, in.w * in.h * 3 * sizeof(float));
    
    • ncnn::Mat multiple channels -> std::vector < cv::Mat > + CV_32FC1
      • You could consume or manipulate ncnn::Mat data directly to avoid data copy
    // ncnn::Mat in(w, h, channels);
    std::vector<cv::Mat> a(in.c);
    for (int p=0; p<in.c; p++)
    {
        a[p] = cv::Mat(in.h, in.w, CV_32FC1);
        memcpy((uchar*)a[p].data, in.channel(p), in.w * in.h * sizeof(float));
    }
    

    二、安卓bitMap与NCNN互转

    使用安卓bitMap,头文件

    #include <android/bitmap.h>
    

    2.1 安卓 to ncnn

    ncnn::Mat in = ncnn::Mat::from_android_bitmap(env, bitmap, ncnn::Mat::PIXEL_BGR);
    

    带resize的

    ncnn::Mat in = ncnn::Mat::from_android_bitmap_resize(env, bitmap, ncnn::Mat::PIXEL_RGB, new_width, new_height)
    

    2.2 ncnn to 安卓

    //out为ncnn的图片
    out.to_android_bitmap(env, bitmap, ncnn::Mat::PIXEL_RGB)
    

    注意,这里参数env表示用地址引用,故对传入的bitmap直接修改,如果不想对原图修改,则在传参时,多传一个输出jobject ouput

    out.to_android_bitmap(env, bitmap, ncnn::Mat::PIXEL_RGB)
    

    [参考]:use ncnn with opencv · Tencent/ncnn Wiki (github.com)

    三、安卓bitMap与Opencv互转

    3.1 源码

    由于opencv编译一般不编译java模块. 关于互转源代码地址在 opencv\sources\modules\java\generator\src\cpp

    gitHub地址:opencv/modules/java/generator/src/cpp at master · opencv/opencv (github.com)

    查看utils.cpp

    如果要编译java,则编译要勾选

    • BUILD_JAVA

    • BUILD_opencv_java

    • BUILD_opencv_java_bingdings_generator

    3.2 方法一

    如果不想编译,直接使用某网友提供的代码:Android使用Opencv图片处理 Mat与Bitmap互转 - 简书 (jianshu.com)
    文件保存为opencv2bitmap.hpp

    #include <opencv2/opencv.hpp>
    #include <android/bitmap.h>
    #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "error", __VA_ARGS__))
    #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "debug", __VA_ARGS__))
    
    void BitmapToMat2(JNIEnv *env, jobject& bitmap, Mat& mat, jboolean needUnPremultiplyAlpha) {
        AndroidBitmapInfo info;
        void *pixels = 0;
        Mat &dst = mat;
    
        try {
            LOGD("nBitmapToMat");
            CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
            CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                      info.format == ANDROID_BITMAP_FORMAT_RGB_565);
            CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
            CV_Assert(pixels);
            dst.create(info.height, info.width, CV_8UC4);
            if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
                LOGD("nBitmapToMat: RGBA_8888 -> CV_8UC4");
                Mat tmp(info.height, info.width, CV_8UC4, pixels);
                if (needUnPremultiplyAlpha) cvtColor(tmp, dst, COLOR_mRGBA2RGBA);
                else tmp.copyTo(dst);
            } else {
                // info.format == ANDROID_BITMAP_FORMAT_RGB_565
                LOGD("nBitmapToMat: RGB_565 -> CV_8UC4");
                Mat tmp(info.height, info.width, CV_8UC2, pixels);
                cvtColor(tmp, dst, COLOR_BGR5652RGBA);
            }
            AndroidBitmap_unlockPixels(env, bitmap);
            return;
        } catch (const cv::Exception &e) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nBitmapToMat catched cv::Exception: %s", e.what());
            jclass je = env->FindClass("org/opencv/core/CvException");
            if (!je) je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, e.what());
            return;
        } catch (...) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nBitmapToMat catched unknown exception (...)");
            jclass je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
            return;
        }
    }
    
    void BitmapToMat(JNIEnv *env, jobject& bitmap, Mat& mat) {
        BitmapToMat2(env, bitmap, mat, false);
    }
    
    void MatToBitmap2
            (JNIEnv *env, Mat& mat, jobject& bitmap, jboolean needPremultiplyAlpha) {
        AndroidBitmapInfo info;
        void *pixels = 0;
        Mat &src = mat;
    
        try {
            LOGD("nMatToBitmap");
            CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
            CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                      info.format == ANDROID_BITMAP_FORMAT_RGB_565);
            CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
                      info.width == (uint32_t) src.cols);
            CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
            CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
            CV_Assert(pixels);
            if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
                Mat tmp(info.height, info.width, CV_8UC4, pixels);
                if (src.type() == CV_8UC1) {
                    LOGD("nMatToBitmap: CV_8UC1 -> RGBA_8888");
                    cvtColor(src, tmp, COLOR_GRAY2RGBA);
                } else if (src.type() == CV_8UC3) {
                    LOGD("nMatToBitmap: CV_8UC3 -> RGBA_8888");
                    cvtColor(src, tmp, COLOR_RGB2RGBA);
                } else if (src.type() == CV_8UC4) {
                    LOGD("nMatToBitmap: CV_8UC4 -> RGBA_8888");
                    if (needPremultiplyAlpha)
                        cvtColor(src, tmp, COLOR_RGBA2mRGBA);
                    else
                        src.copyTo(tmp);
                }
            } else {
                // info.format == ANDROID_BITMAP_FORMAT_RGB_565
                Mat tmp(info.height, info.width, CV_8UC2, pixels);
                if (src.type() == CV_8UC1) {
                    LOGD("nMatToBitmap: CV_8UC1 -> RGB_565");
                    cvtColor(src, tmp, COLOR_GRAY2BGR565);
                } else if (src.type() == CV_8UC3) {
                    LOGD("nMatToBitmap: CV_8UC3 -> RGB_565");
                    cvtColor(src, tmp, COLOR_RGB2BGR565);
                } else if (src.type() == CV_8UC4) {
                    LOGD("nMatToBitmap: CV_8UC4 -> RGB_565");
                    cvtColor(src, tmp, COLOR_RGBA2BGR565);
                }
            }
            AndroidBitmap_unlockPixels(env, bitmap);
            return;
        } catch (const cv::Exception &e) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nMatToBitmap catched cv::Exception: %s", e.what());
            jclass je = env->FindClass("org/opencv/core/CvException");
            if (!je) je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, e.what());
            return;
        } catch (...) {
            AndroidBitmap_unlockPixels(env, bitmap);
            LOGE("nMatToBitmap catched unknown exception (...)");
            jclass je = env->FindClass("java/lang/Exception");
            env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
            return;
        }
    }
    
    void MatToBitmap(JNIEnv *env, Mat& mat, jobject& bitmap) {
        MatToBitmap2(env, mat, bitmap, false);
    }
    

    实现:

    include"opencv2bitmap.hpp"
    

    bitMat转cv::Mat

        Mat mat_image_src ;
        BitmapToMat(env,jsrcBitmap,mat_image_src);//图片转化成mat
    

    cv::Mat转bitMat

        Mat mat_image_dst;
        MatToBitmap(env,mat_image_dst,jsrcBitmap);
    

    问题点:

    由于新ncnn禁用了rtti跟exception,所以上面代码用了try会报错

    error: cannot use 'try' with exceptions disabled
    //或者
    error: cannot use 'throw' with exceptions disabled
    

    解决方法: 改写代码,不用try

    参考(https://www.jianshu.com/p/08dcc910b088)

    3.3 方法二(可行)

    直接在JNI(cpp)文件中加入如下:

    #include <android/bitmap.h>
    
    #include <opencv/cv.hpp>
    #include <opencv2/opencv.hpp>
    #include <opencv2/core.hpp>
    
    #define ASSERT(status, ret)     if (!(status)) { return ret; }
    #define ASSERT_FALSE(status)    ASSERT(status, false)
    
    bool BitmapToMatrix(JNIEnv * env, jobject obj_bitmap, cv::Mat & matrix) {
        void * bitmapPixels;                                            // 保存图片像素数据
        AndroidBitmapInfo bitmapInfo;                                   // 保存图片参数
    
        ASSERT_FALSE( AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0);        // 获取图片参数
        ASSERT_FALSE( bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888
                      || bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565 );          // 只支持 ARGB_8888 和 RGB_565
        ASSERT_FALSE( AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0 );  // 获取图片像素(锁定内存块)
        ASSERT_FALSE( bitmapPixels );
    
        if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels);    // 建立临时 mat
            tmp.copyTo(matrix);                                                         // 拷贝到目标 matrix
        } else {
            cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels);
            cv::cvtColor(tmp, matrix, cv::COLOR_BGR5652RGB);
        }
        AndroidBitmap_unlockPixels(env, obj_bitmap);            // 解锁
        return true;
    }
    bool MatrixToBitmap(JNIEnv * env, cv::Mat & matrix, jobject obj_bitmap) {
        void * bitmapPixels;                                            // 保存图片像素数据
        AndroidBitmapInfo bitmapInfo;                                   // 保存图片参数
    
        ASSERT_FALSE( AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0);        // 获取图片参数
        ASSERT_FALSE( bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888
                      || bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565 );          // 只支持 ARGB_8888 和 RGB_565
        ASSERT_FALSE( matrix.dims == 2
                      && bitmapInfo.height == (uint32_t)matrix.rows
                      && bitmapInfo.width == (uint32_t)matrix.cols );                   // 必须是 2 维矩阵,长宽一致
        ASSERT_FALSE( matrix.type() == CV_8UC1 || matrix.type() == CV_8UC3 || matrix.type() == CV_8UC4 );
        ASSERT_FALSE( AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0 );  // 获取图片像素(锁定内存块)
        ASSERT_FALSE( bitmapPixels );
    
        if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels);
            switch (matrix.type()) {
                case CV_8UC1:   cv::cvtColor(matrix, tmp, cv::COLOR_GRAY2RGBA);     break;
                case CV_8UC3:   cv::cvtColor(matrix, tmp, cv::COLOR_RGB2RGBA);      break;
                case CV_8UC4:   matrix.copyTo(tmp);                                 break;
                default:        AndroidBitmap_unlockPixels(env, obj_bitmap);        return false;
            }
        } else {
            cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels);
            switch (matrix.type()) {
                case CV_8UC1:   cv::cvtColor(matrix, tmp, cv::COLOR_GRAY2BGR565);   break;
                case CV_8UC3:   cv::cvtColor(matrix, tmp, cv::COLOR_RGB2BGR565);    break;
                case CV_8UC4:   cv::cvtColor(matrix, tmp, cv::COLOR_RGBA2BGR565);   break;
                default:        AndroidBitmap_unlockPixels(env, obj_bitmap);        return false;
            }
        }
        AndroidBitmap_unlockPixels(env, obj_bitmap);                // 解锁
        return true;
    }
    
    JNIEXPORT void JNICALL
    Java_com_example_MainActivity_JniBitmapExec(JNIEnv * env, jobject /* this */,
            jobject obj_bitmap, jobject obj_bitmapOut)
    {
        cv::Mat cimage;
        bool ret = BitmapToMatrix(env, obj_bitmap, cimage);          // Bitmap 转 cv::Mat
        if (ret == false) {
            return;
        }
        cv::cvtColor(cimage,cimage,CV_RGBA2BGR); //bitmap格式为RGBA,故需要转为BGR格式
        // 对 mat 进行 opencv 处理
        cv::cvtColor(cimage,cimage,CV_BGR2RGBA); //BGR转回RGBA
        ret = MatrixToBitmap(env, cimage, obj_bitmapOut);       //  cv::Mat 转 Bitmap 
        if (ret == false) {
            return;
        }
    }
    

    四、opecv与SeetaImageData互转

    4.1. opencv to seetaface

    #include <seeta/Common/Struct.h>
    
    cv::Mat cvimage = cv::imread("1.jpg", cv::IMREAD_COLOR);
    SeetaImageData simage;
    simage.width = cvimage.cols;
    simage.height = cvimage.rows;
    simage.channels = cvimage.channels();
    simage.data = cvimage.data;
    

    注意:simage.data拿到的是“借来的”临时数据,在试用的期间,必须保证cvimage的对象不被释放,或者形式的更新。
    注意:原始图像可能是空的,为了输入的合法性,需要通过cvimage.empty()方法,判断图像是否为空。

    这里通过cv::imread并且第二个参数设置为cv::IMREAD_COLOR,获取到的cv::Mat的图像数据是连续存储的,这个是使用SeetaImageData必须的。如果不确定是否是连续存储的对象,可以调用下述代码段进行转换。

    if (!cvimage.isContinuous()) cvimage = cvimage.clone();
    

    4.2 sSeetaImageData to opencv

    cv::Mat another_cvimage = cv::Mat(simage.height, simage.width, CV_8UC(simage.channels), simage.data);
    

    相关文章

      网友评论

          本文标题:图片ncnn、opencv、seeta、android之间互转

          本文链接:https://www.haomeiwen.com/subject/ssdonltx.html