Android पर स्केल की गई आउटपुट फ़ाइल के लिए एक बड़े बिटमैप फ़ाइल का आकार बदलें


218

मेरे पास एक फ़ाइल में एक बड़ा बिटमैप है (3888x2592 कहते हैं)। अब, मैं उस बिटमैप को 800x533 पर आकार देना चाहता हूं और इसे किसी अन्य फ़ाइल में सहेजना चाहता हूं। मैं सामान्य रूप से कॉलिंग Bitmap.createBitmapविधि द्वारा बिटमैप को स्केल करूंगा, लेकिन इसे पहले तर्क के रूप में स्रोत बिटमैप की आवश्यकता है, जो मैं प्रदान नहीं कर सकता क्योंकि बिटमैप ऑब्जेक्ट में मूल छवि लोड करना निश्चित रूप से मेमोरी से अधिक होगा ( उदाहरण के लिए यहां देखें )।

मैं भी साथ बिटमैप नहीं पढ़ उदाहरण के लिए, कर सकते हैं BitmapFactory.decodeFile(file, options), एक प्रदान करने BitmapFactory.Options.inSampleSize, क्योंकि मैं एक सटीक चौड़ाई और ऊंचाई के लिए आकार बदलने के लिए चाहते हैं। का उपयोग करके inSampleSizeबिटमैप को 972x648 (यदि मैं उपयोग करता हूं inSampleSize=4) या 778x518 (यदि मैं उपयोग करता हूं inSampleSize=5, जो कि 2 की शक्ति भी नहीं है) का आकार बदल देगा।

मैं उदाहरण के साथ, इनकंप्लीटाइज़ का उपयोग करके छवि को पढ़ने से बचना चाहूंगा, उदाहरण के लिए, पहले चरण में 972x648 और फिर दूसरे चरण में इसे बिल्कुल 800x533 पर आकार देना, क्योंकि मूल छवि के प्रत्यक्ष आकार की तुलना में गुणवत्ता खराब होगी।

मेरे प्रश्न का सार करने के लिए: क्या एक बड़ी छवि फ़ाइल को 10MP या उससे अधिक पढ़ने और इसे एक नई छवि फ़ाइल में सहेजने का एक तरीका है, जो आउटऑफ़मैरेरी अपवाद प्राप्त किए बिना, एक विशिष्ट नई चौड़ाई और ऊंचाई का आकार देता है?

मैंने भी कोशिश की BitmapFactory.decodeFile(file, options)और Options.outHeight और Options.outWidth मानों को मैन्युअल रूप से 800 और 533 पर सेट करना है, लेकिन यह इस तरह से काम नहीं करता है।


नहीं है, outHeight और outWidth हैं बाहर डीकोड विधि से पैरामीटर। कहा जा रहा है, मेरे पास आपके मुकाबले एक ही मुद्दा है, और मैं कभी भी 2 चरणों के दृष्टिकोण से बहुत संतुष्ट नहीं हूं।
rds

अक्सर, अच्छाई का धन्यवाद, आप कोड की एक पंक्ति का उपयोग कर सकते हैं .. stackoverflow.com/a/17733530/294884
Fattie

पाठकों, कृपया इस बिल्कुल महत्वपूर्ण क्यूए ध्यान दें !!! stackoverflow.com/a/24135522/294884
फेटी

1
Pls ध्यान दें कि यह प्रश्न अब 5 साल पुराना है, और पूर्ण समाधान है .. stackoverflow.com/a/24135522/294884 चीयर्स!
फटी

2
उस विषय पर अब एक आधिकारिक दस्तावेज है: developer.android.com/training/displaying-bitmaps/…
विंस

जवाबों:


146

नहीं। मैं कोई मुझे सही करने के लिए के लिए अच्छा लगेगा, लेकिन मैं लोड / आकार बदलने के दृष्टिकोण आप एक समझौता के रूप में करने की कोशिश की स्वीकार कर लिया।

किसी को ब्राउज़ करने के चरण यहां दिए गए हैं:

  1. अधिकतम संभव गणना करें inSampleSizeजो अभी भी आपके लक्ष्य से बड़ी छवि देता है।
  2. BitmapFactory.decodeFile(file, options)एक विकल्प के रूप में inSampleSize पास करके , छवि का उपयोग करके लोड करें ।
  3. का उपयोग कर वांछित आयामों का आकार बदलें Bitmap.createScaledBitmap()

मैंने उससे बचने की कोशिश की। तो केवल एक कदम में एक बड़ी छवि को सीधे आकार देने का कोई तरीका नहीं है?
मैनुअल

2
मेरी जानकारी के लिए नहीं, लेकिन इसे आगे की खोज करने से न रोकें।
जस्टिन

ठीक है, मैं अब तक अपने स्वीकृत जवाब के लिए इसे ले जाऊंगा। अगर मुझे कोई और तरीका पता चले तो मैं आपको बता दूंगा।
मैनुअल

जैसा कि PSIXO ने एक उत्तर में उल्लेख किया है, आप एंड्रॉइड का उपयोग करना चाह सकते हैं : अगर आप अभी भी इनसप्लेसाइज़ का उपयोग करने के बाद समस्या रखते हैं, तो बड़े।
user276648

बिटमैप चर खाली हो रहा था
प्रसाद

99

जस्टिन कोड के लिए अनुवादित अनुवाद (मेरे लिए सही काम करता है):

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" जैसे चर का उपयोग करते हैं, तो पढ़ना मुश्किल हो जाता है, लेकिन अच्छा उत्तर कम नहीं।
ओलिवर डिक्सन

@Ofir: getImageUri (पाथ); मुझे इस विधि में क्या करना है?
बिगिनर

1
इसके बजाय (w h) /Math.pow (स्केल, 2) का उपयोग करना अधिक कुशल है (w h) >> स्केल।
david.perez

2
System.gc()कृपया कॉल न करें
gw0

धन्यवाद @ ओफ़िर लेकिन यह परिवर्तन छवि अभिविन्यास को संरक्षित नहीं करता है: - /
जोकोलमैन

43

यह ir मोजो रिसिन ’और 'तोर के समाधान’ ’संयुक्त’ है। यह आपको अधिकतम चौड़ाई और अधिकतम ऊंचाई की सीमाओं के साथ आनुपातिक रूप से आकार की छवि देगा।

  1. यह मूल आकार प्राप्त करने के लिए केवल मेटा डेटा पढ़ता है (Options.inJustDecodeBounds)
  2. यह स्मृति को बचाने के लिए एक आकार परिवर्तन का उपयोग करता है (itmap.createScaledBitmap)
  3. यह पहले बनाए गए किसी न किसी बिटमैप के आधार पर एक ठीक आकार वाली छवि का उपयोग करता है।

मेरे लिए यह नीचे 5 मेगापिकेल छवियों पर अच्छा प्रदर्शन कर रहा है।

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

एपीआई का उपयोग क्यों नहीं करते?

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

21
क्योंकि यह मेरी समस्या को हल नहीं करेगा। जो है: "... इसे पहले तर्क के रूप में स्रोत बिटमैप की आवश्यकता है, जो मैं प्रदान नहीं कर सकता क्योंकि मूल छवि को एक बिटमैप ऑब्जेक्ट में लोड करना निश्चित रूप से मेमोरी से अधिक होगा।" इसलिए, मैं एक बिटमैप को पास नहीं कर सकता .createScaledBitmap विधि या तो, क्योंकि मुझे अभी भी एक बड़ी छवि को पहले एक बिटमैप ऑब्जेक्ट में लोड करने की आवश्यकता होगी।
मैनुअल

2
सही। मैंने आपके प्रश्न को फिर से पढ़ा और मूल रूप से (अगर मैं इसे सही समझता हूं) तो यह "मेरी छवि में मूल फ़ाइल लोड किए बिना सटीक आयामों में छवि का आकार बदल सकता है?" यदि ऐसा है - तो मुझे इसका जवाब देने के लिए छवि प्रसंस्करण की जटिलताओं के बारे में पर्याप्त नहीं पता है, लेकिन कुछ मुझे बताता है कि 1. यह एपीआई से उपलब्ध नहीं है, 2. यह 1-लाइनर नहीं होगा। मैं इसे पसंदीदा के रूप में चिह्नित करूंगा - यह देखना दिलचस्प होगा कि क्या आप (या किसी और) इसे हल करेंगे।
बोसोन

इसने मेरे लिए काम किया क्योंकि मैं उड़ी हो रहा हूं और बिटमैप को परिवर्तित कर रहा हूं ताकि उन्हें स्केल करना मेरे लिए 1+ सबसे आसान है।
हमजा

22

अब तक के अन्य उत्कृष्ट उत्तर को स्वीकार करते हुए, मैंने अभी तक इसके लिए जो सबसे अच्छा कोड देखा है, वह फोटो लेने के उपकरण के लिए प्रलेखन में है।

"Decode a Scaled Image" शीर्षक वाला सेक्शन देखें।

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

इसका जो समाधान प्रस्तावित किया गया है, वह यहाँ के अन्य लोगों की तरह आकार बदलने वाला है, लेकिन यह काफी साफ-सुथरा है।

मैंने सुविधा के लिए तैयार फ़ंक्शन के रूप में नीचे दिए गए कोड की प्रतिलिपि बनाई है।

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
पहले आप पूर्णांकों को तैयार कर रहे हैं जो परिणाम को मंजिल देगा। दूसरा कोड targetW या targetH 0 के साथ क्रैश हो जाता है (हालांकि इससे मुझे कोई मतलब नहीं है)। तीसरा inSampleSize की शक्ति 2. होनी चाहिए
साइबरन

मुझे गलत मत समझो यह निश्चित रूप से एक छवि को लोड करेगा, लेकिन यदि फर्श की दीवारों को जोड़ा जाता है, तो यह ऐसा नहीं दिखता है। और यह निश्चित रूप से सही जवाब नहीं है क्योंकि छवि को उम्मीद के मुताबिक नहीं बढ़ाया जाएगा। यह तब तक कुछ नहीं करेगा जब तक कि छवि का दृश्य छवि का आधा आकार या छोटा न हो। तब तक कुछ भी नहीं होता है जब तक कि छवि दृश्य छवि का आकार 1/4 है। और इसलिए दो की शक्तियों के साथ!
सायबरजेन

18

इन उत्तरों और Android प्रलेखन को पढ़ने के बाद यहाँ कोड को मेमोरी में लोड किए बिना बिटमैप का आकार बदलने के लिए है:

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);
}

कृपया ध्यान दें कि bmOptions.inPurgeable = true; पदावनत किया गया है।
रवित

6

जब मेरे पास बड़े बिटमैप होते हैं और मैं उन्हें डीकोड करना चाहता हूं, तो मैं निम्नलिखित का उपयोग करता हूं

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
चूंकि inSampleSize एक इंटेगर है, आप केवल बहुत ही कम पिक्सेल चौड़ाई और ऊँचाई प्राप्त कर सकते हैं जिसे आप प्राप्त करना चाहते हैं। आप कभी-कभी पास हो सकते हैं, लेकिन आप दशमलव से भी बहुत दूर हो सकते हैं।
मैनुअल

सुबह, मैंने आपके कोड (इस धागे में ऊपर पोस्ट) की कोशिश की, लेकिन लगता है कि काम नहीं कर रहा है, मैंने कहां गलत किया? किसी भी सुझाव का स्वागत है :-)
आरआरटीडब्ल्यू

5

यह प्रश्न किसी और के लिए उपयोगी हो सकता है। मैंने जस्टिन के कोड को फिर से लिखा है ताकि विधि को लक्ष्य आकार की वस्तु प्राप्त करने की अनुमति मिल सके। कैनवास का उपयोग करते समय यह बहुत अच्छी तरह से काम करता है। सभी क्रेडिट को अपने महान प्रारंभिक कोड के लिए JUSTIN जाना चाहिए।

    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;
        }
    }

जस्टिन का कोड बड़े बिटमैप्स के साथ काम करने के ओवरहेड को कम करने में बहुत प्रभावी है।


4

मुझे नहीं पता कि मेरा समाधान सबसे अच्छा अभ्यास है, लेकिन मैंने inDensityऔर inTargetDensityविकल्पों का उपयोग करके अपने वांछित स्केलिंग के साथ एक बिटमैप लोड करना हासिल किया । inDensityहै 0जब एक drawable संसाधन को लोड नहीं है, इसलिए इस दृष्टिकोण लोड हो रहा है गैर संसाधन छवियों के लिए है शुरू में।

चर imageUri, maxImageSideLengthऔर contextमेरी विधि के पैरामीटर हैं। मैंने केवल स्पष्टता के लिए AsyncTask को रैप किए बिना केवल विधि कार्यान्वयन को पोस्ट किया।

            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
बहुत अच्छा! Bitmap.createScaledBitmap के बजाय inDensity का उपयोग करने से मुझे बहुत अधिक मेमोरी हीप की बचत हुई। और भी बेहतर inSamplesize के साथ संयुक्त।
ओस्टोकंटेंटन २ '

2

इस बात को ध्यान में रखते हुए कि आप सटीक आकार का आकार बदलना चाहते हैं और आवश्यकतानुसार अधिक से अधिक गुणवत्ता रखना चाहते हैं, मुझे लगता है कि आपको यह प्रयास करना चाहिए।

  1. BitmapFactory.decodeFile के कॉल के साथ आकार की आकृति का पता लगाएं और checkSizeOptions.inJustDecodeBounds प्रदान करें
  2. मेमोरी से अधिक नहीं करने के लिए अपने डिवाइस पर उपयोग किए जा सकने वाले अधिकतम संभव इनसम्प्लाइज़ की गणना करें । बिटमैपाइजइनबीट्स = 2 * चौड़ाई * ऊंचाई; आम तौर पर आपकी तस्वीर के लिए नमूना करें = 2 ठीक होगा क्योंकि आपको केवल 2 * 1944x1296 की आवश्यकता होगी) = 4.8Mbб जो कि स्मृति में होना चाहिए
  3. बिटमैप लोड करने के लिए inSampleSize के साथ BitmapFactory.decodeFile का उपयोग करें
  4. बिटमैप को सटीक आकार में स्केल करें।

प्रेरणा: कई-चरणों का स्केलिंग आपको उच्च गुणवत्ता वाला चित्र दे सकता है, हालांकि इस बात की कोई गारंटी नहीं है कि यह उच्च-नमूनाकरण का उपयोग करने से बेहतर काम करेगा। वास्तव में, मुझे लगता है कि आप एक ऑपरेशन में डायरेक्ट स्केलिंग करने के लिए 5 की तरह इनसप्लासाइज (5 का नहीं पाओ) का उपयोग कर सकते हैं। या सिर्फ 4 का उपयोग करें और फिर आप बस यूआई में उस छवि का उपयोग कर सकते हैं। यदि आप इसे सर्वर पर भेजते हैं - तो आप सर्वर साइड पर सटीक आकार में स्केलिंग कर सकते हैं जो आपको उन्नत स्केलिंग तकनीकों का उपयोग करने की अनुमति देता है।

नोट्स: यदि चरण -3 में लोड किया गया बिटमैप कम से कम 4 गुना बड़ा है (तो 4 * लक्ष्यवधि <चौड़ाई) तो आप बेहतर गुणवत्ता प्राप्त करने के लिए संभवतः कई आकार बदलने का उपयोग कर सकते हैं। जेनेरिक जावा में काम करने वाले कम से कम, एंड्रॉइड में आपके पास http://today.java.net/pub/a/today/2007/04/03/perils-of- स्केलिंग के लिए उपयोग किए गए प्रक्षेप को निर्दिष्ट करने का विकल्प नहीं है। छवि getscaledinstance.html


2

मैंने इस तरह कोड का उपयोग किया:

  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());

मैंने कोशिश की है कि मूल छवि 1230 x 1230 है, और बिटमैप का कहना है कि 330 x 330 है।
और अगर 2590 x 3849 की कोशिश की जाए, तो मुझे OutOfMemoryError मिल जाएगी।

मैंने इसे ट्रेस किया, यह अभी भी OutOfMemoryError को लाइन पर "BitmapFactory.decodeStream (है, अशक्त, विकल्प);", फेंक देता है, यदि मूल बिटमैप बहुत बड़ा है ...


2

उपरोक्त कोड ने थोड़ा क्लीनर बनाया। InputStream के पास अंत में यह सुनिश्चित करने के लिए क्लोज रैपिंग है कि वे बंद हो जाएँ:

* नोट
इनपुट: InputStream है, int w, int h
आउटपुट: बिटमैप

    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

छवि को "सही" तरीके से स्केल करने के लिए, किसी भी पिक्सेल को स्किप किए बिना, आपको डाउन-सैंपलिंग पंक्ति को पंक्ति में करने के लिए छवि डिकोडर में हुक करना होगा। Android (और यह रेखांकित करता है कि Skia पुस्तकालय) इस तरह के कोई हुक प्रदान नहीं करता है, इसलिए आपको अपना स्वयं का रोल करना होगा। मान लें कि आप jpeg इमेजेस के बारे में बात कर रहे हैं, तो आपका सबसे अच्छा शर्त होगा कि आप libjpeg को सीधे C में इस्तेमाल करें।

इसमें शामिल जटिलताओं को देखते हुए, दो-चरणीय सबमिशन-तत्कालीन-पुनर्विक्रय का उपयोग करना संभवतः छवि-पूर्वावलोकन प्रकार के ऐप्स के लिए सबसे अच्छा है।


1

यहाँ एक लेख है जो आकार बदलने के लिए एक अलग दृष्टिकोण लेता है। यह प्रक्रिया में उपलब्ध मेमोरी के आधार पर सबसे बड़े संभावित बिटमैप को मेमोरी में लोड करने और फिर ट्रांसफॉर्म करने का प्रयास करेगा।

http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android/


1

यदि आप पूरी तरह से एक कदम का आकार बदलना चाहते हैं तो आप शायद पूरे बिटमैप को लोड कर सकते हैं यदि Android: bigHeap = true, लेकिन जैसा कि आप देख सकते हैं यह वास्तव में उचित नहीं है।

डॉक्स से: android: bigHeap क्या आपके एप्लिकेशन की प्रक्रियाएं एक बड़े Dalvik हीप के साथ बनाई जानी चाहिए। यह आवेदन के लिए बनाई गई सभी प्रक्रियाओं पर लागू होता है। यह केवल एक प्रक्रिया में लोड किए गए पहले आवेदन पर लागू होता है; यदि आप एक साझा उपयोगकर्ता आईडी का उपयोग कर रहे हैं, तो कई अनुप्रयोगों को एक प्रक्रिया का उपयोग करने की अनुमति देने के लिए, वे सभी को लगातार इस विकल्प का उपयोग करना होगा या उनके अप्रत्याशित परिणाम होंगे। अधिकांश ऐप्स को इसकी आवश्यकता नहीं होनी चाहिए और इसके बजाय बेहतर प्रदर्शन के लिए उनके समग्र मेमोरी उपयोग को कम करने पर ध्यान केंद्रित करना चाहिए। इसे सक्षम करना भी उपलब्ध मेमोरी में निश्चित वृद्धि की गारंटी नहीं देता है, क्योंकि कुछ डिवाइस उनकी कुल उपलब्ध मेमोरी से विवश हैं।



0

इसने मेरे लिए काम किया। फ़ंक्शन एसडी कार्ड पर एक फ़ाइल के लिए एक पथ प्राप्त करता है और अधिकतम प्रदर्शन योग्य आकार में एक बिटमैप लौटाता है। कोड कुछ बदलाव के साथ है, जो कि Ressource के बजाय sd पर छवि फ़ाइल की तरह है और डिस्प्ले ऑब्जेक्ट से witdth और heigth प्राप्त होते हैं।

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

यहां वह कोड है जिसका मैं उपयोग करता हूं जिसमें एंड्रॉइड पर मेमोरी में बड़ी छवियों को डिकोड करने का कोई मुद्दा नहीं है। जब तक मेरा इनपुट पैरामीटर 1024x1024 के आसपास है तब तक मैं छवियों को 20MB तक बड़ा कर सकता हूं। आप दिए गए बिटमैप को किसी अन्य फ़ाइल में सहेज सकते हैं। इस विधि के नीचे एक और विधि है जिसका उपयोग मैं छवियों को नए बिटमैप में स्केल करने के लिए भी करता हूं। अपनी इच्छानुसार इस कोड का उपयोग करने के लिए स्वतंत्र महसूस करें।

/*****************************************************************************
 * 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

ध्यान दें: Methods के पास एक दूसरे के साथ करने के लिए कुछ भी नहीं है सिवाय createScaledBitmap कॉल्स के ऊपर डीकोड विधि। ध्यान दें कि चौड़ाई और ऊँचाई मूल छवि से बदल सकती है।

/*****************************************************************************
 * 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

पैमाने के लिए बिजली की गणना यहाँ गलत है; बस Android डोको पेज पर गणना का उपयोग करें।
फटी

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

या:

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

0

मैं Integer.numberOfLeadingZerosसबसे अच्छा नमूना आकार, बेहतर प्रदर्शन की गणना करने के लिए उपयोग करता हूं ।

कोटलिन में पूर्ण कोड:

@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

निम्न कोड का उपयोग करके बिटमैप का आकार बदलें

    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;
   }    

निम्नलिखित टिप / चाल में भी यही बताया गया है

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.