一、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);
网友评论