Android'de büyük bir bitmap dosyasını ölçekli çıktı dosyasına yeniden boyutlandırma


218

Bir dosyada büyük bir bitmap (örneğin 3888x2592) var. Şimdi, bu bitmap'i 800x533 olarak yeniden boyutlandırmak ve başka bir dosyaya kaydetmek istiyorum. Normalde bitmap'i çağırma Bitmap.createBitmapyöntemiyle ölçeklendiririm, ancak orijinal görüntüyü bir Bitmap nesnesine yüklemek elbette belleği aşacağı için sağlayamadığım ilk argüman olarak bir kaynak bitmap'e ihtiyaç duyar ( örneğin, buraya bakın ).

Ayrıca, ile bitmap okumak örneğin, olamaz BitmapFactory.decodeFile(file, options), bir sağlama BitmapFactory.Options.inSampleSizeben tam bir genişlik ve yüksekliğe yeniden boyutlandırmak istiyorum çünkü. Kullanmak inSampleSizebitmap'i 972x648'e (kullanırsam inSampleSize=4) veya 778x518'e (kullanırsam inSampleSize=5, 2 gücü bile değil ) yeniden boyutlandıracaktır .

Ayrıca, ilk adımda örneğin 972x648 ile inSampleSize kullanarak görüntüyü okumaktan ve daha sonra ikinci adımda tam olarak 800x533'e yeniden boyutlandırmaktan kaçınmak istiyorum, çünkü kalite orijinal görüntünün doğrudan yeniden boyutlandırılmasına kıyasla düşük olacaktır.

Sorumu özetlemek için: 10MP veya daha büyük bir görüntü dosyasını okumanın ve OutOfMemory istisnası olmadan belirli bir yeni genişlik ve yüksekliğe yeniden boyutlandırılan yeni bir görüntü dosyasına kaydetmenin bir yolu var mı?

Ben de denedim BitmapFactory.decodeFile(file, options)ve Options.outHeight ve Options.outWidth değerlerini elle 800 ve 533 olarak ayarladım, ancak bu şekilde çalışmıyor.


Hayır, outHeight ve outWidth vardır dışarı kod çözme yönteminden parametreleri. Bununla birlikte, sizden aynı meseleye sahibim ve şimdiye kadar 2 adım yaklaşımından pek memnun değilim.
rds

sık sık, çok şükür, bir satır kod kullanabilirsiniz .. stackoverflow.com/a/17733530/294884
Fattie

Okuyucular, bu kesinlikle kritik KG'ye dikkat edin !!! stackoverflow.com/a/24135522/294884
Fattie

1
Pls bu sorunun şimdi 5 yaşında olduğunu ve tam çözüm olduğunu .. stackoverflow.com/a/24135522/294884 Şerefe!
Fattie

2
Artık bu konuyla ilgili resmi bir belge var: developer.android.com/training/displaying-bitmaps/…
Vince

Yanıtlar:


146

Hayır . Birinin beni düzeltmesini isterdim, ama uzlaşma olarak denediğiniz yük / yeniden boyutlandırma yaklaşımını kabul ettim.

İşte göz atan herkes için adımlar:

  1. inSampleSizeHala hedefinizden daha büyük bir görüntü veren maksimum değeri hesaplayın .
  2. BitmapFactory.decodeFile(file, options)Örnek olarak bir örnek olarak ileterek görüntüyü yükleyin .
  3. Düğmesini kullanarak istenen boyutlara yeniden boyutlandırın Bitmap.createScaledBitmap().

Bundan kaçınmaya çalıştım. Yani büyük bir görüntüyü yalnızca bir adımda doğrudan yeniden boyutlandırmanın bir yolu yok mu?
Manuel

2
Bildiğim kadarıyla değil, ama bunu daha fazla keşfetmenizi engellemeyin.
Justin

Tamam, bunu şimdiye kadar kabul edilen cevabım için alacağım. Başka bir yöntem bulursam size bildiririm.
Manuel

Psixo bir cevapta belirtildiği gibi, olabilir de android kullanmak istiyorum: Hâlâ inSampleSize kullandıktan sonra sorunları var largeHeap eğer.
user276648 17:15

bitmap değişkeni
Prasad

99

Justin cevabı koda çevrildi (benim için mükemmel çalışıyor):

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();



    int scale = 1;
    while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", 
       orig-height: " + options.outHeight);

    Bitmap resultBitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        options = new BitmapFactory.Options();
        options.inSampleSize = scale;
        resultBitmap = BitmapFactory.decodeStream(in, null, options);

        // resize to desired dimensions
        int height = resultBitmap.getHeight();
        int width = resultBitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, 
           (int) y, true);
        resultBitmap.recycle();
        resultBitmap = scaledBitmap;

        System.gc();
    } else {
        resultBitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + 
       resultBitmap.getHeight());
    return resultBitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

15
"B" gibi değişkenler kullandığınızda okumayı zorlaştırır, ancak daha az olmayan iyi yanıtlar.
Oliver Dixon

@Ofir: getImageUri (yol); bu yöntemde geçmek zorundayım?
Biginner

1
(W h) /Math.pow (ölçek, 2) yerine (w h) >> ölçeğini kullanmak daha verimlidir .
david.perez

2
System.gc()Lütfen arama
gw0

Teşekkürler @Ofir ama bu dönüşüm görüntü yönünü
korumuyor

43

Bu 'Mojo Risin'in ve' Ofir'in çözümlerinin "birleşimidir". Bu size maksimum genişlik ve maksimum yükseklik sınırlarıyla orantılı olarak yeniden boyutlandırılmış bir görüntü verecektir.

  1. Orijinal boyutu almak için yalnızca meta verileri okur (options.inJustDecodeBounds)
  2. Belleği kaydetmek için bir yeniden boyutlandırma kullanır (itmap.createScaledBitmap)
  3. Daha önce oluşturulan kaba Bitamp'a dayanan kesin olarak yeniden boyutlandırılmış bir görüntü kullanır.

Benim için aşağıdaki 5 MegaPixel görüntüde iyi performans gösteriyor.

try
{
    int inWidth = 0;
    int inHeight = 0;

    InputStream in = new FileInputStream(pathOfInputImage);

    // decode image size (decode metadata only, not the whole image)
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();
    in = null;

    // save width and height
    inWidth = options.outWidth;
    inHeight = options.outHeight;

    // decode full image pre-resized
    in = new FileInputStream(pathOfInputImage);
    options = new BitmapFactory.Options();
    // calc rought re-size (this is no exact resize)
    options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
    // decode full image
    Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

    // calc exact destination size
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
    RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

    // resize bitmap
    Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    // save image
    try
    {
        FileOutputStream out = new FileOutputStream(pathOfOutputImage);
        resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    }
    catch (Exception e)
    {
        Log.e("Image", e.getMessage(), e);
    }
}
catch (IOException e)
{
    Log.e("Image", e.getMessage(), e);
}

23

Neden API kullanmıyorsunuz?

int h = 48; // height in pixels
int w = 48; // width in pixels    
Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);

21
Çünkü sorunumu çözmezdi. Bu da: "... ilk argüman olarak bir kaynak bitmap'e ihtiyaç duyuyor, ki bunu sağlayamıyorum çünkü orijinal görüntüyü bir Bitmap nesnesine yüklemek elbette belleği aşıyor." Yani, hala bir Bitmap'i .createScaledBitmap yöntemine geçiremiyorum, çünkü yine de önce bir Bitmap nesnesine büyük bir görüntü yüklemem gerekiyor.
Manuel

2
Sağ. Sorunuzu tekrar okudum ve temelde (doğru anlarsam) "Orijinal dosyayı belleğe yüklemeden resmi tam boyutlara yeniden boyutlandırabilir miyim?" Eğer öyleyse - Bunu cevaplamak için görüntü işleme karışıklıkları hakkında yeterli bilmiyorum ama bir şey söylüyor ki 1. API mevcut değildir, 2. 1-astar olmayacak. Bunu favori olarak işaretleyeceğim - sizin (veya başka birinin) bunu çözüp çözmeyeceğini görmek ilginç olurdu.
Bostone

benim için işe yaradı çünkü ben uri alıyorum ve bitmap dönüştürmek böylece onları ölçekleme benim için en kolay 1 + kolay.
Hamza

22

Şimdiye kadarki diğer mükemmel yanıtı kabul ederek, bunun için henüz gördüğüm en iyi kod fotoğraf çekme aracının belgelerinde.

"Ölçekli Görüntünün Kodunu Çözme" başlıklı bölüme bakın.

http://developer.android.com/training/camera/photobasics.html

Önerdiği çözüm, buradaki diğerleri gibi bir yeniden boyutlandırma ve ölçeklendirme çözümüdür.

Kolaylık sağlamak için aşağıdaki kodu hazır bir işlev olarak kopyaladım.

private void setPic(String imagePath, ImageView destination) {
    int targetW = destination.getWidth();
    int targetH = destination.getHeight();
    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    destination.setImageBitmap(bitmap);
}

1
İlk önce sonucu ne katacak tamsayılar ayırıyorsunuz. İkincisi, kod targetW veya targetH 0 ile çöküyor (her ne kadar bu pek mantıklı değil). Üçüncü inSampleSize 2'nin gücü olmalıdır.
sibergen

Beni yanlış anlamayın. Bu kesinlikle bir görüntü yükleyecektir, ancak eğer döşeme döşenirse, öyle görünmez. Ve bu kesinlikle doğru cevap değil çünkü görüntü beklendiği gibi ölçeklenmeyecek. Görüntü görünümü görüntünün yarısı veya daha küçük olana kadar hiçbir şey yapmaz. Ardından görüntü görünümü görüntünün 1 / 4'ü kadar hiçbir şey olmaz. Ve böylece ikisinin güçleriyle!
cybergen

18

Bu cevapları ve android belgelerini okuduktan sonra , bitmap'i belleğe yüklemeden yeniden boyutlandırmak için kod:

public Bitmap getResizedBitmap(int targetW, int targetH,  String imagePath) {

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    //inJustDecodeBounds = true <-- will not load the bitmap into memory
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    return(bitmap);
}

Lütfen bmOptions.inPurgeable = true; kullanımdan kaldırıldı.
Ravit

6

Büyük bitmap'lere sahip olduğumda ve bunları yeniden boyutlandırmak istediğimde aşağıdakileri kullanıyorum

BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);

1
İnSampleSize bir Tamsayı olduğundan, almak istediğiniz tam piksel genişliğini ve yüksekliğini nadiren elde edersiniz. Bazen yakınlaşabilirsiniz, ancak ondalık sayılara bağlı olarak ondan uzak da olabilirsiniz.
Manuel

Sabah, kodunuzu denedim (bu yazıda yukarıdaki yazı), ancak çalışmıyor gibi görünüyor, nerede yanlış yaptım? Herhangi bir
öneri

5

Bu, bu soruya bakan başka birisi için yararlı olabilir. Ben de yöntemin gerekli hedef boyutu nesnesi almak için Justin'in kodunu yeniden yazdım. Tuval'i kullanırken bu çok işe yarar. Tüm krediler harika ilk kodu için JUSTIN'a gitmelidir.

    private Bitmap getBitmap(int path, Canvas canvas) {

        Resources resource = null;
        try {
            final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
            resource = getResources();

            // Decode image size
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(resource, path, options);

            int scale = 1;
            while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
                  IMAGE_MAX_SIZE) {
               scale++;
            }
            Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

            Bitmap pic = null;
            if (scale > 1) {
                scale--;
                // scale to max possible inSampleSize that still yields an image
                // larger than target
                options = new BitmapFactory.Options();
                options.inSampleSize = scale;
                pic = BitmapFactory.decodeResource(resource, path, options);

                // resize to desired dimensions
                int height = canvas.getHeight();
                int width = canvas.getWidth();
                Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

                double y = Math.sqrt(IMAGE_MAX_SIZE
                        / (((double) width) / height));
                double x = (y / height) * width;

                Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
                pic.recycle();
                pic = scaledBitmap;

                System.gc();
            } else {
                pic = BitmapFactory.decodeResource(resource, path);
            }

            Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
            return pic;
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(),e);
            return null;
        }
    }

Justin'in kodu büyük Bitmap'lerle çalışma yükünü azaltmada ÇOK etkilidir.


4

Çözümümün en iyi uygulama olup olmadığını bilmiyorum, ancak inDensityve inTargetDensityseçeneklerini kullanarak istediğiniz ölçeklendirmeyle bir bitmap yüklemeyi başardım . inDensitydır-dir0 bu yaklaşım yükleme olmayan kaynak görüntüler için yani, bir çizilebilir kaynağı yüklenirken değilken başlangıçta.

Değişkenler imageUri, maxImageSideLengthve contextbenim yönteminin parametrelerdir. Açıklık için AsyncTask'ı sarmadan sadece yöntem uygulamasını yayınladım.

            ContentResolver resolver = context.getContentResolver();
            InputStream is;
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Options opts = new Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, opts);

            // scale the image
            float maxSideLength = maxImageSideLength;
            float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
            // do not upscale!
            if (scaleFactor < 1) {
                opts.inDensity = 10000;
                opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
            }
            opts.inJustDecodeBounds = false;

            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }

            return bitmap;

2
Çok hoş! Bitmap.createScaledBitmap yerine inDensity kullanmak beni çok fazla bellek yığını kurtardı. Daha da iyi inSamplesize ile birleştirildi.
Ostkontentitan

2

Tam boyuta yeniden boyutlandırmak ve gerektiği kadar kaliteyi korumak istediğinizi göz önünde bulundurarak bunu denemeniz gerektiğini düşünüyorum.

  1. BitmapFactory.decodeFile çağrısı ile ve checkSizeOptions.inJustDecodeBounds ile yeniden boyutlandırılan görüntünün boyutunu öğrenin
  2. Belleği aşmamak için cihazınızda kullanabileceğiniz olası maksimumSizeSize içinde hesaplayın . bitmapSizeInBytes = 2 * genişlik * yükseklik; GenellikleSampleSize = 2'deki resminiz için iyi olurdu, çünkü sadece 2 * 1944x1296) = 4.8Mbб gerekir
  3. Bitmap'i yüklemek için inSampleSize ile BitmapFactory.decodeFile kullanın
  4. Bitmap'i tam boyuta ölçeklendirin.

Motivasyon: çok aşamalı ölçekleme size daha yüksek kaliteli görüntü verebilir, ancak yüksek inSampleSize kullanmaktan daha iyi çalışacağının garantisi yoktur. Aslında, bir işlemde doğrudan ölçeklendirme için 5 gibi (2 pow değil) gibi inSampleSize kullanabilirsiniz düşünüyorum. Ya da sadece 4 kullanın ve sonra bu görüntüyü kullanıcı arayüzünde kullanabilirsiniz. sunucuya gönderirseniz - gelişmiş ölçekleme tekniklerini kullanmanızı sağlayan sunucu tarafında tam boyutta ölçeklendirme yapabilirsiniz.

Notlar: 3. adımda yüklenen Bitmap en az 4 kat daha büyükse (yani 4 * targetWidth <genişlik) daha iyi kalite elde etmek için muhtemelen birkaç yeniden boyutlandırma kullanabilirsiniz. en azından jenerik java'da çalışıyor, android'de ölçekleme için kullanılan enterpolasyonu belirtme seçeneğiniz yok http://today.java.net/pub/a/today/2007/04/03/perils-of- görüntü getscaledinstance.html


2

Ben böyle kod kullanılır:

  String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg";
  BitmapFactory.Options options=new BitmapFactory.Options();
  InputStream is=new FileInputStream(filePath);
  BitmapFactory.decodeStream(is, null, options);
  is.close();
  is=new FileInputStream(filePath);
  // here w and h are the desired width and height
  options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired...
  // bmp is the resized bitmap
  Bitmap bmp=BitmapFactory.decodeStream(is, null, options);
  is.close();
  Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());

Orijinal görüntünün 1230 x 1230 olduğunu denedim ve bitmap var 330 x 330 diyor.
Ve denedim 2590 x 3849, ben OutOfMemoryError var.

Ben izledim, hala OutOfMemoryError "BitmapFactory.decodeStream (null, options);", orijinal bitmap çok büyükse ...


2

Yukarıdaki kod biraz daha temiz. InputStreams, kapanmalarını sağlamak için nihayetinde yakın bir sargıya sahiptir:

* Not
Giriş: InputStream, int w, int h
Çıkış: Bitmap

    try
    {

        final int inWidth;
        final int inHeight;

        final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");

        {

            final FileOutputStream tempOut = new FileOutputStream(tempFile);

            StreamUtil.copyTo(is, tempOut);

            tempOut.close();

        }



        {

            final InputStream in = new FileInputStream(tempFile);
            final BitmapFactory.Options options = new BitmapFactory.Options();

            try {

                // decode image size (decode metadata only, not the whole image)
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            // save width and height
            inWidth = options.outWidth;
            inHeight = options.outHeight;

        }

        final Bitmap roughBitmap;

        {

            // decode full image pre-resized
            final InputStream in = new FileInputStream(tempFile);

            try {

                final BitmapFactory.Options options = new BitmapFactory.Options();
                // calc rought re-size (this is no exact resize)
                options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                // decode full image
                roughBitmap = BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            tempFile.delete();

        }

        float[] values = new float[9];

        {

            // calc exact destination size
            Matrix m = new Matrix();
            RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
            RectF outRect = new RectF(0, 0, w, h);
            m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
            m.getValues(values);

        }

        // resize bitmap
        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

        return resizedBitmap;

    }
    catch (IOException e) {

        logger.error("Error:" , e);
        throw new ResourceException("could not create bitmap");

    }

1

Görüntüyü "doğru" şekilde ölçeklemek için, herhangi bir pikseli atlamadan, aşağı örneklemeyi satır satır gerçekleştirmek için görüntü kod çözücüye bağlamanız gerekir. Android (ve altında yatan Skia kütüphanesi) böyle bir kanca sağlamaz, bu yüzden kendinizinkini yuvarlamanız gerekir. Jpeg görüntülerinden bahsettiğinizi varsayarsak, en iyi seçiminiz libjpeg'i doğrudan C'de kullanmak olacaktır.

İlgili karmaşıklıklar göz önüne alındığında, iki adımlı alt örnek-sonra yeniden ölçeklendirmeyi kullanmak, muhtemelen görüntü önizleme türü uygulamalar için en iyisidir.



1

Kesinlikle bir adım yeniden boyutlandırmak istiyorsanız, android: largeHeap = true ise muhtemelen tüm bitmap'i yükleyebilirsiniz, ancak gördüğünüz gibi bu gerçekten tavsiye edilmez.

Dokümanlar: android: largeHeap Uygulamanızın işlemlerinin büyük bir Dalvik yığınıyla oluşturulup oluşturulmayacağı. Bu, uygulama için oluşturulan tüm işlemler için geçerlidir. Yalnızca bir sürece yüklenen ilk uygulama için geçerlidir; birden çok uygulamanın bir işlemi kullanmasına izin vermek için paylaşılan bir kullanıcı kimliği kullanıyorsanız, hepsinin bu seçeneği tutarlı bir şekilde kullanması gerekir veya öngörülemeyen sonuçları olur. Çoğu uygulamanın buna ihtiyacı olmamalı ve bunun yerine daha iyi performans için genel bellek kullanımını azaltmaya odaklanmalıdır. Bunu etkinleştirmek de kullanılabilir bellekte sabit bir artışı garanti etmez, çünkü bazı cihazlar toplam kullanılabilir bellekleriyle sınırlıdır.



0

Bu benim için çalıştı. İşlev sd karttaki bir dosyaya giden yolu alır ve görüntülenebilir maksimum boyutta bir Bitmap döndürür. Kod Ofir'den sd'deki görüntü dosyası gibi bazı değişikliklerle bir Ressource ve witdth ve heigth, Display Object'den elde edilir.

private Bitmap makeBitmap(String path) {

    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        //resource = getResources();

        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        int scale = 1;
        while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
                IMAGE_MAX_SIZE) {
            scale++;
        }
        Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

        Bitmap pic = null;
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            pic = BitmapFactory.decodeFile(path, options);

            // resize to desired dimensions

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.y;
            int height = size.x;

            //int height = imageView.getHeight();
            //int width = imageView.getWidth();
            Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

            double y = Math.sqrt(IMAGE_MAX_SIZE
                    / (((double) width) / height));
            double x = (y / height) * width;

            Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
            pic.recycle();
            pic = scaledBitmap;

            System.gc();
        } else {
            pic = BitmapFactory.decodeFile(path);
        }

        Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
        return pic;

    } catch (Exception e) {
        Log.e("TAG", e.getMessage(),e);
        return null;
    }

}

0

İşte Android'de bellekte büyük görüntüleri çözme ile ilgili herhangi bir sorun olmayan kullandığım kod. Giriş parametrelerim 1024x1024 civarında olduğu sürece 20MB'den daha büyük görüntüleri deşifre edebildim. Döndürülen bitmap'i başka bir dosyaya kaydedebilirsiniz. Bu yöntemin altında, görüntüleri yeni bir bitmap'e ölçeklemek için de kullandığım başka bir yöntem var. Bu kodu istediğiniz gibi kullanmaktan çekinmeyin.

/*****************************************************************************
 * public decode - decode the image into a Bitmap
 * 
 * @param xyDimension
 *            - The max XY Dimension before the image is scaled down - XY =
 *            1080x1080 and Image = 2000x2000 image will be scaled down to a
 *            value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image - a value of "null" if there is an issue decoding
 *         image dimension
 * 
 * @throws FileNotFoundException
 *             - If the image has been removed while this operation is
 *             taking place
 */
public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException
{
    // The Bitmap to return given a Uri to a file
    Bitmap bitmap = null;
    File file = null;
    FileInputStream fis = null;
    InputStream in = null;

    // Try to decode the Uri
    try
    {
        // Initialize scale to no real scaling factor
        double scale = 1;

        // Get FileInputStream to get a FileDescriptor
        file = new File( this.imageUri.getPath() );

        fis = new FileInputStream( file );
        FileDescriptor fd = fis.getFD();

        // Get a BitmapFactory Options object
        BitmapFactory.Options o = new BitmapFactory.Options();

        // Decode only the image size
        o.inJustDecodeBounds = true;
        o.inPreferredConfig = bitmapConfig;

        // Decode to get Width & Height of image only
        BitmapFactory.decodeFileDescriptor( fd, null, o );
        BitmapFactory.decodeStream( null );

        if( o.outHeight > xyDimension || o.outWidth > xyDimension )
        {
            // Change the scale if the image is larger then desired image
            // max size
            scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) );
        }

        // Decode with inSampleSize scale will either be 1 or calculated value
        o.inJustDecodeBounds = false;
        o.inSampleSize = (int) scale;

        // Decode the Uri for real with the inSampleSize
        in = new BufferedInputStream( fis );
        bitmap = BitmapFactory.decodeStream( in, null, o );
    }
    catch( OutOfMemoryError e )
    {
        Log.e( DEBUG_TAG, "decode : OutOfMemoryError" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "decode : NullPointerException" );
        e.printStackTrace();
    }
    catch( RuntimeException e )
    {
        Log.e( DEBUG_TAG, "decode : RuntimeException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "decode : FileNotFoundException" );
        e.printStackTrace();
    }
    catch( IOException e )
    {
        Log.e( DEBUG_TAG, "decode : IOException" );
        e.printStackTrace();
    }

    // Save memory
    file = null;
    fis = null;
    in = null;

    return bitmap;

} // decode

Not: Yukarıdaki createScaledBitmap çağrıları kod çözme yöntemi dışında yöntemlerin birbirleri ile ilgisi yoktur. Not genişlik ve yükseklik orijinal görüntüden değişebilir.

/*****************************************************************************
 * public createScaledBitmap - Creates a new bitmap, scaled from an existing
 * bitmap.
 * 
 * @param dstWidth
 *            - Scale the width to this dimension
 * @param dstHeight
 *            - Scale the height to this dimension
 * @param xyDimension
 *            - The max XY Dimension before the original image is scaled
 *            down - XY = 1080x1080 and Image = 2000x2000 image will be
 *            scaled down to a value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image scaled - a value of "null" if there is an issue
 * 
 */
public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig )
{
    Bitmap scaledBitmap = null;

    try
    {
        Bitmap bitmap = this.decode( xyDimension, bitmapConfig );

        // Create an empty Bitmap which will contain the new scaled bitmap
        // This scaled bitmap should be the size we want to scale the
        // original bitmap too
        scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig );

        float ratioX = dstWidth / (float) bitmap.getWidth();
        float ratioY = dstHeight / (float) bitmap.getHeight();
        float middleX = dstWidth / 2.0f;
        float middleY = dstHeight / 2.0f;

        // Used to for scaling the image
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale( ratioX, ratioY, middleX, middleY );

        // Used to do the work of scaling
        Canvas canvas = new Canvas( scaledBitmap );
        canvas.setMatrix( scaleMatrix );
        canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) );
    }
    catch( IllegalArgumentException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" );
        e.printStackTrace();
    }

    return scaledBitmap;
} // End createScaledBitmap

ölçek için güç hesaplaması burada yanlıştır; sadece android doco sayfasındaki hesaplamayı kullanın.
Fattie

0
 Bitmap yourBitmap;
 Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

veya:

 resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);

0

kullanırım Integer.numberOfLeadingZerosEn iyi örnek boyutunu, daha iyi performansı hesaplamak için .

Kotlin kodunun tamamı:

@Throws(IOException::class)
fun File.decodeBitmap(options: BitmapFactory.Options): Bitmap? {
    return inputStream().use {
        BitmapFactory.decodeStream(it, null, options)
    }
}

@Throws(IOException::class)
fun File.decodeBitmapAtLeast(
        @androidx.annotation.IntRange(from = 1) width: Int,
        @androidx.annotation.IntRange(from = 1) height: Int
): Bitmap? {
    val options = BitmapFactory.Options()

    options.inJustDecodeBounds = true
    decodeBitmap(options)

    val ow = options.outWidth
    val oh = options.outHeight

    if (ow == -1 || oh == -1) return null

    val w = ow / width
    val h = oh / height

    if (w > 1 && h > 1) {
        val p = 31 - maxOf(Integer.numberOfLeadingZeros(w), Integer.numberOfLeadingZeros(h))
        options.inSampleSize = 1 shl maxOf(0, p)
    }
    options.inJustDecodeBounds = false
    return decodeBitmap(options)
}

-2

Aşağıdaki kodu kullanarak bitmap'i yeniden boyutlandırın

    public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;        
    BitmapFactory.decodeFile(file.getPath(), options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(file.getPath(), options);
   }

    private 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) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     }

     return inSampleSize;
   }    

Aynı şey aşağıdaki ipucunda da açıklanmaktadır.

http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.