Preservar la calidad de la imagen cuando decodifique el flujo en android

Tengo una imagen en sdcard y necesito mostrarla en la vista de la imagen

El problema es que después de haber sido decodificado, parece que la calidad se deteriora. ¿Hay alguna manera de mantener la calidad y al mismo tiempo preservar la memoria?

¿O, si utilizé una imagen más grande, hay alguna manera de preservar la memoria (evitar el mapa de bits demasiado grande para cargar) con escala? (Necesito mantener el tamaño de la imagen original)

Gracias por ayudar.

public Bitmap decodeFile(String pubKey, int bookPageID, int type) throws IOException { Bitmap b = null; File f = null; String uri = null; FileInputStream fis = null; Log.d(TAG,"pageID to read: " + bookPageID); IRIssue issue = Broker.model.issueDataStore.getIRIssue(pubKey); String imageFolder = IRConstant.issueFolder(issue.year, issue.month, issue.day, issue.pubKey); // pageID - 1 since the page is an array (start at 0) , but page ID start at 1 if (type == 2){ uri = imageFolder + issue.vol[0].pages[bookPageID - 1].graphicUri; }else { uri = imageFolder + issue.vol[0].pages[bookPageID - 1].textUri; } f = new File(uri); Log.d(TAG,"is file: " + uri + " exist?" + f.exists()); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; fis = new FileInputStream(f); b = BitmapFactory.decodeStream(fis, null, options); fis.close(); return b; } 

El siguiente código utiliza varios conceptos de Mostrar mapas de bits de manera eficiente
En primer lugar la lectura de mapa de bits se realiza en un hilo de fondo, estoy usando marca / reset en inputStream (envuelto con BufferedInputstream ) para no leer más de lo necesario de la corriente cuando tratamos de averiguar el tamaño de la imagen para usar al calcular la escala factor. El código de ejemplo a continuación sub-muestra la imagen para que coincida con un tamaño de 320 x 240 píxeles. En un código de no ejemplo uno podría tener interfaz de devolución de llamada simple enviar el mapa de bits de onPostExecute a implementar clase (implementador de interfaz de devolución de llamada). O proporcione la vista como un miembro al AsyncTask directamente y establezca el mapa de bits en onPostExecute .

Llame al código con (ejemplo de imagen descargada en mi dispositivo):

 BitmapTask task = new BitmapTask(getContentResolver()); task.execute(Uri.parse("file:///storage/emulated/0/Download/download.jpg")); 

Las clases en cuestión

 private static class BitmapTask extends AsyncTask<Uri, Void, Bitmap> { // prevent mem leaks private WeakReference<ContentResolver> mWeakContentResolver; public BitmapTask(ContentResolver resolver) { mWeakContentResolver = new WeakReference<ContentResolver>(resolver); } @Override protected Bitmap doInBackground(Uri... params) { Bitmap bitmap = null; ContentResolver resolver = mWeakContentResolver.get(); if (resolver != null) { BufferedInputStream stream = null; try { stream = new BufferedInputStream( resolver.openInputStream(params[0])); stream.mark(1 * 1024); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // Find out size of image BitmapFactory.decodeStream(stream, null, options); try { stream.reset(); } catch (IOException e) { Log.d(TAG, "reset failed"); } int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType; Log.d(TAG, "w, h, mime " + imageWidth + " , " + imageHeight + " , " + imageType); options.inJustDecodeBounds = false; // Calculate down scale factor options.inSampleSize = calculateInSampleSize(options, 320, 240); return BitmapFactory.decodeStream(stream, null, options); } catch (FileNotFoundException e) { bitmap = null; } finally { IOUtils.closeStreamSilently(stream); } } return bitmap; } @Override protected void onPostExecute(Bitmap result) { Log.d(TAG, "bitmap result: " + ((result != null) ? "" + result.getByteCount() : "0")); result.recycle(); } } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and // keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } 

Editar: Para los grandes inputstreams podría haber un problema con la marca / reset técnica, SkImageDecoder::Factory returned null se puede ver en los registros a veces, resultando en mapa de bits nulo, se otra pregunta SO sobre el asunto: SkImageDecoder :: Factory devuelto null . Se puede arreglar reiniciando la variable de flujo de nuevo stream = new resolver.openInputStream(params[0])); antes de regresar en doInBackground

Editar 2: Si tiene que conservar el tamaño de la imagen, pero no quiere limitar el uso de la memoria, puede usar las options.inPreferredConfig = Bitmap.Config.RGB_565; que reduce a la mitad la memoria por píxel, pero tener en cuenta que las imágenes podrían no tener una gran calidad (experimento!).

Utilizo una clase BitmapHandler personalizada para resolver este problema:

 public class BitmapHandler { private static int IMAGE_MAX_SIZE = 540; //This can be set to whatever you see fit private static String TAG = "BitmapHandler.java"; public BitmapHandler(Context ctx){ WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; int height = size.y; Log.v(TAG, "Screen width: " + width + " height: " + height); IMAGE_MAX_SIZE = (Math.min(width, height))*4; //Try playing with this multiplier number to get different degrees of scaling } public Bitmap decodeFileAsPath(String uri) { // Create a file out of the uri File f = null; Log.v(TAG, "Incoming uri: " + uri); f = new File(uri); if (f.equals(null)){ Log.v(TAG, "File is null!"); } return decodeFile(f); } private Bitmap decodeFile(File f) { Bitmap b = null; try { // Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; o.inScaled = false; FileInputStream fis = new FileInputStream(f); BitmapFactory.decodeStream(fis, null, o); fis.close(); int scale = 1; Log.v(TAG, "Decode Image height: " + o.outHeight + " and width: " + o.outWidth); if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) { scale = (int) Math.pow( 2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5))); } Log.v(TAG, "Final scale: " + scale); // Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inScaled = false; o2.inSampleSize = scale; fis = new FileInputStream(f); b = BitmapFactory.decodeStream(fis, null, o2); fis.close(); } catch (IOException e) { Log.v(TAG, e.getMessage()); } return b; } } 

Esto escala dinámicamente su imagen mientras intenta evitar una excepción OutOfMemory

BitmapFactory tiene una propiedad inSampleSize que fue diseñada para resolver este problema. Consulte los documentos: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize

Este artículo escribe acerca de cómo manejar mapas de bits eficientemente: http://developer.android.com/training/displaying-bitmaps/index.html

Mientras que la decodificación agrega options.injustdecodeBounds = true porque eso demuestra que usted quiere solamente los límites no el mapa de bits entero. Esto evitará los errores de memoria porque sólo cargará el tamaño de imagen que realmente necesita.

La segunda cosa es escalar ese mapa de bits según su necesidad y para hacer eso sin la distorsión usted tiene que escalarlo de una manera que mantenga la proporción de aspecto. Al hacer clic en la imagen, se establece una ración fija en la que se hace clic en la imagen y que la escala de esa imagen en esa proporción sólo. Si no está en su mano de lo que puede utilizar el siguiente método para obtener el tamaño insample y que decodificar la imagen a un tamaño particular sin distorsión.

 private int calculateSampleSize(int width, int height, int targetWidth, int targetHeight) { float bitmapWidth = width; float bitmapHeight = height; int bitmapResolution = (int) (bitmapWidth * bitmapHeight); int targetResolution = targetWidth * targetHeight; int sampleSize = 1; if (targetResolution == 0) { return sampleSize; } for (int i = 1; (bitmapResolution / i) > targetResolution; i *= 2) { sampleSize = i; } return sampleSize; 

}

Proporcione cualquier comentario si encuentra alguna mejora.

Un enfoque rápido que también es altamente configurable es utilizar un WebView lugar de un ImageView :

 WebView mWebView = (WebView) findViewById(R.id.webview); mWebView.getSettings().setAllowFileAccess(true); mWebView.getSettings().setBuiltInZoomControls(true); String base = Environment.getExternalStorageDirectory().getAbsolutePath().toString(); String imagePath = "file://" + base + "/myImage.png";//replace with the name of the image you are accessing String html = "<html><head></head><body><img src=\"" + imagePath + "\"></body></html>"; mWebView.loadDataWithBaseURL("", html, "text/html","utf-8", ""); 

GridViewActivity.java

 public class GridViewActivity extends Activity implements OnItemClickListener { private String[] filepathstring; private File[] listfile; GridView grid_sdcard; File file; ImageView image; GridViewAdapter adapter; int select; int sele; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.gridview_activity); image=(ImageView)convertView.findViewById(R.id.image_show); grid_sdcard=(GridView)findViewById(R.id.grid_sdcard); if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(this,"Error! No SDCARD Found!", Toast.LENGTH_LONG).show(); } else { file=new File(Environment.getExternalStorageDirectory() + File.separator +"eMENU Images/"); file.mkdirs(); Toast.makeText(GridViewActivity.this,"Past Image Here:", Toast.LENGTH_LONG).show(); } if(file.isDirectory()) { listfile=file.listFiles(); for(int i=0;i<listfile.length;i++) { filepathstring[i]=listfile[i].getAbsolutePath(); } } adapter=new GridViewAdapter(GridViewActivity.this,filepathstring); grid_sdcard.setAdapter(adapter); grid_sdcard.setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id1) { final String image=filepathstring[position]; Bitmap bitmap=BitmapFactory.decodeFile(filepathlist[position]); imageshow.setImageBitmap(bitmap); } } 

GridViewAdapter.java

 public class GridViewAdapter extends BaseAdapter { String[] filepathlist; Context context; public GridViewAdapter(Context con, String[] filepathstring) { context=con; filepathlist=filepathstring; } @Override public int getCount() { return filepathlist.length; } @Override public Object getItem(int position) { // TODO Auto-generated method stub return position; } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView==null) { LayoutInflater inflater=(LayoutInflater)convertView.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView=inflater.inflate(R.layout.griadview_adapter,null); } ImageView imageshow=(ImageView)convertView.findViewById(R.id.image_show); Bitmap bitmap=BitmapFactory.decodeFile(filepathlist[position]); imageshow.setImageBitmap(bitmap); return convertView; } } 
  • ¿Cuál es la relación entre el lienzo y la matriz en Android?
  • Poner un mapa de bits en un paquete
  • Cambiar el tamaño de un mapa de bits a un valor fijo, pero sin cambiar la relación de aspecto
  • ¿Cómo crear Bitmap de la imagen de buffer de bytes en grises?
  • Obtener imagen uri de picasso?
  • Subir una foto tomada por la cámara a un servidor con tamaño limitado
  • Android - Desvanece imagen de mapa de bits en lienzo
  • Referencia de mapa de bits desconocida publicada - Marcador de configuración en android
  • Android - dibujar mapa de bits dentro de la lona
  • Cómo comprimir la imagen de Uri a mapa de bits
  • Last.fm no devolverá la imagen del artista
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.