2010-05-21 10 views
6

Ich versuche, ein Bild von der SD-Karte (in Emulator) zu lesen und dann mit derAndroid - liest PNG-Bild ohne Alpha und Decodierung als ARGB_8888

BitmapFactory.decodeByteArray

Methode ein Bitmap-Bild erstellen. Ich habe die Optionen:

options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false

Dann extrahieren ich die Pixel in einen ByteBuffer.

ByteBuffer buffer = ByteBuffer.allocateDirect(width*height*4)
bitmap.copyPixelsToBuffer(buffer)

Ich benutze diese ByteBuffer dann in der JNI es in RGB-Format zu konvertieren und auf sie berechnet werden soll.

Aber immer bekomme ich falsche Daten - ich teste, ohne den ByteBuffer zu ändern. Ich tue es nur, um es in die native Methode in JNI zu bringen. Dann werfen Sie es in eine unsigned char* und konvertieren Sie es zurück in eine ByteBuffer, bevor Sie es wieder nach Java zurückgeben.

unsigned char* buffer = (unsinged char*)(env->GetDirectBufferAddress(byteBuffer))
jobject returnByteBuffer = env->NewDirectByteBuffer(buffer, length)

Vor Anzeige des Bildes I-Daten wieder mit

bitmap.copyPixelsFromBuffer(buffer)

Aber dann hat es in ihm falsche Daten.

Meine Frage ist, wenn dies daran liegt, dass das Bild intern in RGB 565 konvertiert wird oder was ist hier falsch?

.....

eine Antwort für sich:

- >>> ja, wird es intern zu RGB565 umgewandelt.

Weiß jemand, wie man ein solches Bitmapbild von PNG mit ARGB8888 Pixelformat erzeugt?

Wenn jemand eine Idee hat, wäre es großartig!

+0

Der Punkt ist, dass ich die Bilddaten benötigen, um darauf in einem nativen Code-Teil zu berechnen. Zum Testen (Emulator) nehme ich das PNG-Bild von der SD-Karte und dann nehme ich die Bildsequenz von der Android-Kamera. Ich möchte 24Bit Bilder keine Informationen verlieren, bevor Sie darauf ... PS: wo ist der Commant des anderen Kerl gegangen? – user345982

+1

ok, ich werde einen anderen Weg versuchen. Ich möchte den schnellsten Weg machen, um die Bilddaten aus einem Bild zu bekommen. Danach werde ich einige Bildverarbeitungen an den Rohdaten vornehmen. Was ist der schnellste Weg, um die Bildpixelinformation in ein Byte [] zu extrahieren? Grüße, F. – user345982

Antwort

11

Ein ARGB_8888 Bitmap (auf Pre-Honeycomb-Versionen) wird nativ im RGBA-Format gespeichert. So wird der Alphakanal am Ende verschoben. Sie sollten dies berücksichtigen, wenn Sie nativ auf die Pixel eines Bitmaps zugreifen.

Ich nehme an, Sie Code für eine Version von Android schreiben niedriger als 3,2 (API-Ebene < 12), denn seitdem das Verhalten der Methoden

BitmapFactory.decodeFile(pathToImage); 
BitmapFactory.decodeFile(pathToImage, opt); 
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/); 

geändert hat.

Auf älteren Plattformen (API-Ebene < 12) versuchen die Methoden BitmapFactory.decodeFile (..) standardmäßig ein Bitmap mit RGB_565-Konfig zurückzugeben, wenn sie kein Alpha finden, was die Qualität einer Datei verringert.Dies ist immer noch ok, weil Sie eine ARGB_8888 Bitmap erzwingen können

options.inPrefferedConfig = Bitmap.Config.ARGB_8888 
options.inDither = false 

Das eigentliche Problem mit kommt, wenn jedes Pixel des Bildes hat einen Alpha-Wert von 255 (das heißt vollständig undurchsichtig). In diesem Fall wird das Bitmap-Flag 'hasAlpha' auf 'false' gesetzt, obwohl Ihr Bitmap über ARGB_8888 config verfügt. Wenn Ihre * .png-Datei mindestens ein echtes transparentes Pixel hätte, wäre dieses Flag auf "true" gesetzt worden und Sie müssten sich um nichts kümmern.

Also, wenn Sie wollen einen skalierten Bitmap erstellen

bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/); 

prüft das Verfahren mit, ob das Flag ‚hasAlpha‘ auf true oder false gesetzt ist, und in Ihrem Fall ist es auf false gesetzt, was dazu führt, Ermitteln einer skalierten Bitmap, die automatisch in das Format RGB_565 konvertiert wurde.

daher auf API-Ebene> = 12 gibt es eine öffentliche Methode

public void setHasAlpha (boolean hasAlpha); 

genannt, die dieses Problem gelöst haben würde. Bis jetzt war das nur eine Erklärung für das Problem. Ich habe etwas recherchiert und festgestellt, dass die setHasAlpha-Methode schon lange existiert und öffentlich ist, aber versteckt wurde (@hide annotation). Hier ist, wie es auf Android 2.3 definiert ist:

/** 
* Tell the bitmap if all of the pixels are known to be opaque (false) 
* or if some of the pixels may contain non-opaque alpha values (true). 
* Note, for some configs (e.g. RGB_565) this call is ignore, since it does 
* not support per-pixel alpha values. 
* 
* This is meant as a drawing hint, as in some cases a bitmap that is known 
* to be opaque can take a faster drawing case than one that may have 
* non-opaque per-pixel alpha values. 
* 
* @hide 
*/ 
public void setHasAlpha(boolean hasAlpha) { 
    nativeSetHasAlpha(mNativeBitmap, hasAlpha); 
} 

Jetzt hier ist mein Lösungsvorschlag. Es beinhaltet kein Kopieren von Bitmap-Daten:

  1. zur Laufzeit überprüft mit java.lang.reflect, wenn die aktuellen Bitmap Implementierung eine öffentliche ‚setHasAplha‘ Methode hat. (Nach meinen Tests funktioniert es perfekt seit API Level 3, und ich habe keine niedrigeren Versionen getestet, weil JNI nicht funktionieren würde). Sie haben möglicherweise Probleme, wenn ein Hersteller es ausdrücklich als privat, geschützt oder gelöscht markiert hat.

  2. Rufen Sie die Methode 'setHasAlpha' für ein bestimmtes Bitmap-Objekt mit JNI auf. Dies funktioniert perfekt, auch für private Methoden oder Felder. Es ist offiziell, dass JNI nicht prüft, ob Sie die Zugriffskontrollregeln verletzen oder nicht. Quelle: http://java.sun.com/docs/books/jni/html/pitfalls.html (10.9) Dies gibt uns große Macht, die mit Bedacht verwendet werden sollte. Ich würde nicht versuchen, ein letztes Feld zu modifizieren, selbst wenn es funktionieren würde (um nur ein Beispiel zu geben). Und bitte beachten Sie, das ist nur eine Abhilfe ...

Hier ist meine Umsetzung aller notwendigen Methoden ist:

JAVA TEIL:

// NOTE: this cannot be used in switch statements 
    private static final boolean SETHASALPHA_EXISTS = setHasAlphaExists(); 

    private static boolean setHasAlphaExists() { 
     // get all puplic Methods of the class Bitmap 
     java.lang.reflect.Method[] methods = Bitmap.class.getMethods(); 
     // search for a method called 'setHasAlpha' 
     for(int i=0; i<methods.length; i++) { 
      if(methods[i].getName().contains("setHasAlpha")) { 
       Log.i(TAG, "method setHasAlpha was found"); 
       return true; 
      } 
     } 
     Log.i(TAG, "couldn't find method setHasAlpha"); 
     return false; 
    } 

    private static void setHasAlpha(Bitmap bitmap, boolean value) { 
     if(bitmap.hasAlpha() == value) { 
      Log.i(TAG, "bitmap.hasAlpha() == value -> do nothing"); 
      return; 
     } 

     if(!SETHASALPHA_EXISTS) { // if we can't find it then API level MUST be lower than 12 
      // couldn't find the setHasAlpha-method 
      // <-- provide alternative here... 
      return; 
     } 

     // using android.os.Build.VERSION.SDK to support API level 3 and above 
     // use android.os.Build.VERSION.SDK_INT to support API level 4 and above 
     if(Integer.valueOf(android.os.Build.VERSION.SDK) <= 11) { 
      Log.i(TAG, "BEFORE: bitmap.hasAlpha() == " + bitmap.hasAlpha()); 
      Log.i(TAG, "trying to set hasAplha to true"); 
      int result = setHasAlphaNative(bitmap, value); 
      Log.i(TAG, "AFTER: bitmap.hasAlpha() == " + bitmap.hasAlpha()); 

      if(result == -1) { 
       Log.e(TAG, "Unable to access bitmap."); // usually due to a bug in the own code 
       return; 
      } 
     } else { //API level >= 12 
      bitmap.setHasAlpha(true); 
     } 
    } 

    /** 
    * Decodes a Bitmap from the SD card 
    * and scales it if necessary 
    */ 
    public Bitmap decodeBitmapFromFile(String pathToImage, int pixels_limit) { 
     Bitmap bitmap; 

     Options opt = new Options(); 
     opt.inDither = false; //important 
     opt.inPreferredConfig = Bitmap.Config.ARGB_8888; 
     bitmap = BitmapFactory.decodeFile(pathToImage, opt); 

     if(bitmap == null) { 
      Log.e(TAG, "unable to decode bitmap"); 
      return null; 
     } 

     setHasAlpha(bitmap, true); // if necessary 

     int numOfPixels = bitmap.getWidth() * bitmap.getHeight(); 

     if(numOfPixels > pixels_limit) { //image needs to be scaled down 
      // ensures that the scaled image uses the maximum of the pixel_limit while keeping the original aspect ratio 
      // i use: private static final int pixels_limit = 1280*960; //1,3 Megapixel 
      imageScaleFactor = Math.sqrt((double) pixels_limit/(double) numOfPixels); 
      Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 
        (int) (imageScaleFactor * bitmap.getWidth()), (int) (imageScaleFactor * bitmap.getHeight()), false); 

      bitmap.recycle(); 
      bitmap = scaledBitmap; 

      Log.i(TAG, "scaled bitmap config: " + bitmap.getConfig().toString()); 
      Log.i(TAG, "pixels_limit = " + pixels_limit); 
      Log.i(TAG, "scaled_numOfpixels = " + scaledBitmap.getWidth()*scaledBitmap.getHeight()); 

      setHasAlpha(bitmap, true); // if necessary 
     } 

     return bitmap; 
    } 

Ihre lib Laden und die native Methode deklarieren:

static { 
    System.loadLibrary("bitmaputils"); 
} 

private static native int setHasAlphaNative(Bitmap bitmap, boolean value); 

Mutter Abschnitt ('jni' Ordner)

Android.mk

:

LOCAL_PATH := $(call my-dir) 

include $(CLEAR_VARS) 
LOCAL_MODULE := bitmaputils 
LOCAL_SRC_FILES := bitmap_utils.c 
LOCAL_LDLIBS := -llog -ljnigraphics -lz -ldl -lgcc 
include $(BUILD_SHARED_LIBRARY) 

bitmapUtils.c:

#include <jni.h> 
#include <android/bitmap.h> 
#include <android/log.h> 

#define LOG_TAG "BitmapTest" 
#define Log_i(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 
#define Log_e(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 


// caching class and method IDs for a faster subsequent access 
static jclass bitmap_class = 0; 
static jmethodID setHasAlphaMethodID = 0; 

jint Java_com_example_bitmaptest_MainActivity_setHasAlphaNative(JNIEnv * env, jclass clazz, jobject bitmap, jboolean value) { 
    AndroidBitmapInfo info; 
    void* pixels; 


    if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) { 
     Log_e("Failed to get Bitmap info"); 
     return -1; 
    } 

    if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { 
     Log_e("Incompatible Bitmap format"); 
     return -1; 
    } 

    if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) { 
     Log_e("Failed to lock the pixels of the Bitmap"); 
     return -1; 
    } 


    // get class 
    if(bitmap_class == NULL) { //initializing jclass 
     // NOTE: The class Bitmap exists since API level 1, so it just must be found. 
     bitmap_class = (*env)->GetObjectClass(env, bitmap); 
     if(bitmap_class == NULL) { 
      Log_e("bitmap_class == NULL"); 
      return -2; 
     } 
    } 

    // get methodID 
    if(setHasAlphaMethodID == NULL) { //initializing jmethodID 
     // NOTE: If this fails, because the method could not be found the App will crash. 
     // But we only call this part of the code if the method was found using java.lang.Reflect 
     setHasAlphaMethodID = (*env)->GetMethodID(env, bitmap_class, "setHasAlpha", "(Z)V"); 
     if(setHasAlphaMethodID == NULL) { 
      Log_e("methodID == NULL"); 
      return -2; 
     } 
    } 

    // call java instance method 
    (*env)->CallVoidMethod(env, bitmap, setHasAlphaMethodID, value); 

    // if an exception was thrown we could handle it here 
    if ((*env)->ExceptionOccurred(env)) { 
     (*env)->ExceptionDescribe(env); 
     (*env)->ExceptionClear(env); 
     Log_e("calling setHasAlpha threw an exception"); 
     return -2; 
    } 

    if(AndroidBitmap_unlockPixels(env, bitmap) < 0) { 
     Log_e("Failed to unlock the pixels of the Bitmap"); 
     return -1; 
    } 

    return 0; // success 
} 

Das ist es. Wir sind fertig. Ich habe den gesamten Code zum Kopieren und Einfügen veröffentlicht. Der tatsächliche Code ist nicht so groß, aber all diese paranoiden Fehlerprüfungen machen es viel größer. Ich hoffe, dass dies für jeden hilfreich sein könnte.

+0

gibt es keine Alternative für die setHasAlpha-Methode für API-Ebenen von weniger als 12? Ich habe ein Gerät, das das gleiche Problem hat, und leider 2.3.6 –

+0

sorry, ich lese den jni Teil, es ist in der jni getan :) –

+0

aber wo wird die Methode aufgerufen, um alpha auf wahr für 2.3 Versionen –