大多数时候,在android上获得OOM是由于使用了太多的位图和/或创建了大的位图。
最近,我决定试用JNI,以便通过将数据本身存储在JNI端来避免OOM。
使用JNI和NDK旋转位图
这一次,我增加了存储,恢复,裁剪和旋转位图的能力。它应该很容易添加更多的选项,我会很高兴如果其他人在这里将自己的代码添加到更有用的功能。
所以我将要展示的代码实际上是我创建的所有东西的合并。
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
final int width=bitmap.getWidth(),height=bitmap.getHeight();
// store the bitmap in the JNI "world"
final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
// no need for the bitmap on the java "world", since the operations are done on the JNI "world"
bitmap.recycle();
// crop a center square from the bitmap, from (0.25,0.25) to (0.75,0.75) of the bitmap.
bitmapHolder.cropBitmap(width/4,height/4,width*3/4,height*3/4);
//rotate the bitmap:
bitmapHolder.rotateBitmapCcw90();
//get the output java bitmap , and free the one on the JNI "world"
bitmap=bitmapHolder.getBitmapAndFree();
还原
旋转90度CCW
庄稼。
人脸检测
任意角度的旋转,或者至少是明显的旋转。目前我只增加了90度的旋转计数器时钟明智。
注意:这是一个有点旧的代码。对于最新的一个,请查看GitHub上的project页面。
LOCAL_PATH := $(call my-dir)
#bitmap operations module
include $(CLEAR_VARS)
LOCAL_MODULE := JniBitmapOperations
LOCAL_SRC_FILES := JniBitmapOperations.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics
include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g
#if you need to add more module, do the same as the one we started with (the one with the CLEAR_VARS)
#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>
#define LOG_TAG "DEBUG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C"
{
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle);
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom);
}
class JniBitmap
{
public:
uint32_t* _storedBitmapPixels;
AndroidBitmapInfo _bitmapInfo;
JniBitmap()
{
_storedBitmapPixels = NULL;
}
};
/**crops the bitmap within to be smaller. note that no validations are done*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
uint32_t* previousData = jniBitmap->_storedBitmapPixels;
uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
uint32_t newWidth = right - left, newHeight = bottom - top;
uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
uint32_t* whereToGet = previousData + left + top * oldWidth;
uint32_t* whereToPut = newBitmapPixels;
for (int y = top; y < bottom; ++y)
{
memcpy(whereToPut, whereToGet, sizeof(uint32_t) * newWidth);
whereToGet += oldWidth;
whereToPut += newWidth;
}
//done copying , so replace old data with new one
delete[] previousData;
jniBitmap->_storedBitmapPixels = newBitmapPixels;
jniBitmap->_bitmapInfo.width = newWidth;
jniBitmap->_bitmapInfo.height = newHeight;
}
/**rotates the inner bitmap data by 90 degress counter clock wise*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
uint32_t* previousData = jniBitmap->_storedBitmapPixels;
AndroidBitmapInfo bitmapInfo = jniBitmap->_bitmapInfo;
uint32_t* newBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
int whereToPut = 0;
// A.D D.C
// ...>...
// B.C A.B
for (int x = bitmapInfo.width - 1; x >= 0; --x)
for (int y = 0; y < bitmapInfo.height; ++y)
{
uint32_t pixel = previousData[bitmapInfo.width * y + x];
newBitmapPixels[whereToPut++] = pixel;
}
delete[] previousData;
jniBitmap->_storedBitmapPixels = newBitmapPixels;
uint32_t temp = bitmapInfo.width;
bitmapInfo.width = bitmapInfo.height;
bitmapInfo.height = temp;
}
/**free bitmap*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
delete[] jniBitmap->_storedBitmapPixels;
jniBitmap->_storedBitmapPixels = NULL;
delete jniBitmap;
}
/**restore java bitmap (from JNI data)*/ //
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
{
LOGD("no bitmap data was stored. returning null...");
return NULL;
}
//
//creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
//
//LOGD("creating new bitmap...");
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jstring configName = env->NewStringUTF("ARGB_8888");
jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, jniBitmap->_bitmapInfo.width, jniBitmap->_bitmapInfo.height, bitmapConfig);
//
// putting the pixels into the new bitmap:
//
int ret;
void* bitmapPixels;
if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
{
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return NULL;
}
uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
int pixelsCount = jniBitmap->_bitmapInfo.height * jniBitmap->_bitmapInfo.width;
memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels, sizeof(uint32_t) * pixelsCount);
AndroidBitmap_unlockPixels(env, newBitmap);
//LOGD("returning the new bitmap");
return newBitmap;
}
/**store java bitmap as JNI data*/ //
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap)
{
AndroidBitmapInfo bitmapInfo;
uint32_t* storedBitmapPixels = NULL;
//LOGD("reading bitmap info...");
int ret;
if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0)
{
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return NULL;
}
LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);
if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
{
LOGE("Bitmap format is not RGBA_8888!");
return NULL;
}
//
//read pixels of bitmap into native memory :
//
//LOGD("reading bitmap pixels...");
void* bitmapPixels;
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0)
{
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return NULL;
}
uint32_t* src = (uint32_t*) bitmapPixels;
storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
int pixelsCount = bitmapInfo.height * bitmapInfo.width;
memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelsCount);
AndroidBitmap_unlockPixels(env, bitmap);
JniBitmap *jniBitmap = new JniBitmap();
jniBitmap->_bitmapInfo = bitmapInfo;
jniBitmap->_storedBitmapPixels = storedBitmapPixels;
return env->NewDirectByteBuffer(jniBitmap, 0);
}
package com.jni.bitmap_operations;
import java.nio.ByteBuffer;
import android.graphics.Bitmap;
import android.util.Log;
public class JniBitmapHolder
{
ByteBuffer _handler =null;
static
{
System.loadLibrary("JniBitmapOperations");
}
private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);
private native Bitmap jniGetBitmapFromStoredBitmapData(ByteBuffer handler);
private native void jniFreeBitmapData(ByteBuffer handler);
private native void jniRotateBitmapCcw90(ByteBuffer handler);
private native void jniCropBitmap(ByteBuffer handler,final int left,final int top,final int right,final int bottom);
public JniBitmapHolder()
{}
public JniBitmapHolder(final Bitmap bitmap)
{
storeBitmap(bitmap);
}
public void storeBitmap(final Bitmap bitmap)
{
if(_handler!=null)
freeBitmap();
_handler=jniStoreBitmapData(bitmap);
}
public void rotateBitmapCcw90()
{
if(_handler==null)
return;
jniRotateBitmapCcw90(_handler);
}
public void cropBitmap(final int left,final int top,final int right,final int bottom)
{
if(_handler==null)
return;
jniCropBitmap(_handler,left,top,right,bottom);
}
public Bitmap getBitmap()
{
if(_handler==null)
return null;
return jniGetBitmapFromStoredBitmapData(_handler);
}
public Bitmap getBitmapAndFree()
{
final Bitmap bitmap=getBitmap();
freeBitmap();
return bitmap;
}
public void freeBitmap()
{
if(_handler==null)
return;
jniFreeBitmapData(_handler);
_handler=null;
}
@Override
protected void finalize() throws Throwable
{
super.finalize();
if(_handler==null)
return;
Log.w("DEBUG","JNI bitmap wasn't freed nicely.please rememeber to free the bitmap as soon as you can");
freeBitmap();
}
}
我有一些图像只包含数字和分号。 您可以在这里看到更多:https://imgur.com/a/54dsl6h 它们在我看来非常干净和简单,但Tesseract认为它们是空的“页面”()。 我怎么做才能让Tesseract更好地识别角色?
html2canvas生成图片时,原HTML的图片被放大
我的python项目(appspots.com)的图像有一个很大的问题。 日志上说: 信息2012-04-22 17:29:42,601 dev_appserver.py:2884]“Get/css/images/ui-bg_glass_75_e6e6e6_1x400.png HTTP/1.1”404- 我的app.yaml(仅处理程序): 我希望你能帮助我:)
我知道在Excel VBA中使用和不是最佳实践。我已经看到了关于在处理范围时如何避免它们的参考资料(例如:链接)。在处理(或者一般范围以外的任何东西)时,如何避免它们? 例如,使用和修改y轴上的最大值的方法如下所示(这很有效): 为了避免使用和,我已经尽力声明变量并使用这些变量,但这并不起作用: 编辑:让它与这个“冗长的”版本一起工作:
在线安装 从 http://mirrors.ustc.edu.cn/qtproject/official_releases/online_installers/ 下载在线安装器。 禁用默认源,添加新源 http://mirrors.ustc.edu.cn/qtproject/online/qtsdkrepository/linux_x64/root/qt/ (其他版本注意更改地址)
使用科大镜像更新插件 以 Luna 为例,点击 Help → Install New Software... → Available Software Sites 可以看到所有的更新源,将其中的 download.eclipse.org 全部替换成 mirrors.ustc.edu.cn/eclipse 即可。 基本配置 Vim-like 快捷键绑定 详见 http://vrapper.sourc
使用说明 在第一次执行 install.package("xxx") 时会弹出窗口让你选择镜像,此时选择 China Hefei 即可。 也可以通过 chooseCRANmirror() 来打开窗口。 相关链接 官方主页: http://cran.r-project.org/ FAQ: http://cran.r-project.org/faqs.html 文档: http://cran.r-pr
使用说明 若你以前从未使用过 CPAN,请在命令行下运行 cpan Would you like me to automatically choose some CPAN mirror sites for you? (This means connecting to the Internet) [yes] 此处输入 no Would you like to pick from the CPAN