android ndk opencv传入jpeg图像后绘制图形显示异常

idea_wj 2018-01-08 01:03:15
我是使用纯粹ndk进行操作的,目的是要给传入的图像正中间画一个圆。
从android的java层使用Bitmap(格式A8888)传入一张图像到c层,当使用png图像时,一切正常,但是使用jpeg图像,返回的结果却不正常,求大神指导,具体如下:
lena图是png图,中间的透明圆显示正常

下面这个是jpeg风景图,中间画的圆显示却异常:

以下是ndk的代码:

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_wj_demo_ui_activity_DemoBaseImageProcessActivity_mask(JNIEnv *env, jclass obj,jint width,jint height,jintArray srcPixels) {
jint *cbuf;
cbuf = env->GetIntArrayElements(srcPixels,JNI_FALSE);
if(cbuf == NULL)
return NULL;
Mat img(width, height,CV_8UC4,cbuf);

circle(img,Point(width/2,height/2),100,Scalar(255,255,255,0),-1);

int size = width*height;
jint *ptr = img.ptr<jint>(0);
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result,0,size,ptr);
img.release();
env->ReleaseIntArrayElements(srcPixels,cbuf,0);
return result;
}

以下是android java层传入图像:

private void mask(){
mSrcBitmap = ((BitmapDrawable)(mIvSrc.getDrawable())).getBitmap();
mSrcBitmapWidth = mSrcBitmap.getWidth();
mSrcBitmapHeight = mSrcBitmap.getHeight();
mSrcBitmapPixels = new int[mSrcBitmapWidth*mSrcBitmapHeight];
mTmpBitmap = Bitmap.createBitmap(mSrcBitmapWidth,mSrcBitmapHeight,Bitmap.Config.ARGB_8888);
mSrcBitmap.getPixels(mSrcBitmapPixels,0,mSrcBitmapWidth,0,0,mSrcBitmapWidth,mSrcBitmapHeight);

int[] px = mask(mSrcBitmapWidth,mSrcBitmapHeight,mSrcBitmapPixels);
mTmpBitmap.setPixels(px,0,mSrcBitmapWidth,0,0,mSrcBitmapWidth,mSrcBitmapHeight);
mIvSrc.setImageBitmap(mTmpBitmap);

}

private native int[] mask(int width, int height, int[] pixels);
...全文
492 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
idea_wj 2018-01-10
  • 打赏
  • 举报
回复
发现根本问题所在了,创建Mat时,是opencv自动设置步长的,android端它的步长=最短边*4,因此会造成显示异常。
我把步长改为width*4,结果好很多了,只是不知道为什么,绘制出的圆,在横坐标值大于图像最短边的区域,就不进行绘制了。。。。好多坑。。。。
代码:

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_wj_demo_ui_activity_DemoBaseImageProcessActivity_mask(JNIEnv *env, jclass obj,jint width,jint height,jintArray srcPixels) {
jint *cbuf;
cbuf = env->GetIntArrayElements(srcPixels,JNI_FALSE);
if(cbuf == NULL)
return NULL;
Mat img(width, height,CV_8UC4,cbuf,width*4); //要手动加入步长,不能使用自动步长,自动步长会设置为最短边*4

//中间圆不全,暂不清楚为什么要以最短边为界限,超过最短边的区域,不会被绘制
circle(img,Point(width/2,height/2),200,Scalar(0,0,0,0),-1);

int size = width*height;
jint *ptr = img.ptr<jint>(0);
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result,0,size,ptr);
img.release();
env->ReleaseIntArrayElements(srcPixels,cbuf,0);
return result;
}

显示结果
csulizhang 2018-01-10
  • 打赏
  • 举报
回复
我觉得用opencv操作的话,还是把ARGB转成BGR格式,再进行处理会好一些,也具有通用性一些,不然你这样处理,挪到IOS上又不适用了
idea_wj 2018-01-10
  • 打赏
  • 举报
回复
经过刚刚测试,针对上述问题,现给出最根本的解决方式:
楼主太粗心了。。。把长宽给搞反了,Mat中rows和cols对应的要输入Bitmap中的height和width。。。颠倒一下即可,不用步长设置。。。
具体代码如下:

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_wj_demo_ui_activity_DemoBaseImageProcessActivity_mask(JNIEnv *env, jclass obj,jint width,jint height,jintArray srcPixels) {
jint *cbuf;
cbuf = env->GetIntArrayElements(srcPixels,JNI_FALSE);
if(cbuf == NULL)
return NULL;
Mat img(height, width,CV_8UC4,cbuf); //要先传入height,再传入width,步长自动即可

circle(img,Point(width/2,height/2),200,Scalar(0,0,0,0),-1);

int size = width*height;
jint *ptr = img.ptr<jint>(0);
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result,0,size,ptr);
img.release();
env->ReleaseIntArrayElements(srcPixels,cbuf,0);
return result;
}

最终正确结果:

醉了,到最后居然犯了这么个大错。。
赵4老师 2018-01-09
  • 打赏
  • 举报
回复
引用 2 楼 idea_wj 的回复:
楼上的,这么回复不太好吧?
我一向如此。
idea_wj 2018-01-09
  • 打赏
  • 举报
回复
引用 5 楼 zhangli00 的回复:
img.release();你不要release掉看看,因为你本来就不是用的指针,是分配了内存的,release掉了也可能没有


谢谢昨天的建议,不过还是不行。。。
今天实验了一下自己的猜测,可能是opencv的Mat颜色空间解析异常造成的,下面两种方式测试代码:
//第一种方式,使用img.at<Vec4b>(i,j)[3]=0;来设置透明度,显示异常

int cX = width/2;
int cY = height/2;
for(int i=0;i<width;++i)
for(int j=0;j<height;++j){
int dx = cX-i;
int dy = cY-j;
if(abs(dx*dx+dy*dy)<=100*100){
img.at<Vec4b>(i,j)[3]=0;
}
}

显示异常:


第2种方式,直接使用uchar* outPtr = img.ptr(0);修改像素值,显示正常:

int cX = width/2;
int cY = height/2;
uchar* outPtr = img.ptr(0);
for(int i = 0; i < width*height; i ++){
int dx = cX-i%width;
int dy = cY-i/width;
if(abs(dx*dx+dy*dy)<=100*100){
outPtr[4*i+3] = 0;
}
}

结果正常:



第二个处理方式有效,但不是最优,最根本但解决方式还是不确定。。。
赵4老师 2018-01-08
  • 打赏
  • 举报
回复
代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒:再牛×的老师也无法代替学生自己领悟和上厕所! 单步调试和设断点调试(VS IDE中编译连接通过以后,按F10或F11键单步执行,按Shift+F11退出当前函数;在某行按F9设断点后按F5执行停在该断点处。)是程序员必须掌握的技能之一。
csulizhang 2018-01-08
  • 打赏
  • 举报
回复
img.release();你不要release掉看看,因为你本来就不是用的指针,是分配了内存的,release掉了也可能没有
idea_wj 2018-01-08
  • 打赏
  • 举报
回复
谢谢哈 试了一下,结果还是一样。。。最初我是把修改后的像素结果映射给一个传入的数组参数中来避免内存释放问题的,结果也一样。。。 一会儿我试试直接用android提供的AndroidBitmapInfo.
csulizhang 2018-01-08
  • 打赏
  • 举报
回复
我之前用jni opencv做图片裁切的时候遇到过类似的问题,通过ROI裁出来的图片就像你这样,原因就是ROI只是指向内存,而并非复制内存,在返回的时候内存值清空了,建议你这样尝试一下:env->SetIntArrayRegion(result, 0, size, (jint*)img.data);
idea_wj 2018-01-08
  • 打赏
  • 举报
回复
楼上的,这么回复不太好吧?

4,246

社区成员

发帖
与我相关
我的任务
社区描述
OpenCV相关技术交流专区
计算机视觉人工智能opencv 技术论坛(原bbs) 广东省·深圳市
社区管理员
  • OpenCV
  • 幻灰龙
  • OpenCV中国团队
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

OpenCV中国团队官方博客:https://blog.csdn.net/opencvchina

试试用AI创作助手写篇文章吧