“ บิตแมปที่ใหญ่เกินไปที่จะอัปโหลดลงในพื้นผิว”


154

ฉันกำลังโหลดบิตแมปไปยัง ImageView และเห็นข้อผิดพลาดนี้ ฉันรวบรวมขีด จำกัด นี้เกี่ยวข้องกับการ จำกัด ขนาดสำหรับพื้นผิวฮาร์ดแวร์ OpenGL (2048x2048) รูปภาพที่ฉันต้องโหลดคือภาพขนาดย่อ / ขยายที่สูงประมาณ 4,000 พิกเซล

ฉันได้ลองปิดการเร่งด้วยฮาร์ดแวร์ในรายการ แต่ไม่มีความสุข

    <application
        android:hardwareAccelerated="false"
        ....
        >

เป็นไปได้ไหมที่จะโหลดรูปภาพที่มีขนาดใหญ่กว่า 2048 พิกเซลลงใน ImageView


1
สำหรับทุกคนที่ดูที่นี่อย่าลืมที่จะใส่ภาพของคุณในมุมมองเลื่อนถ้าคุณต้องการที่จะเลื่อน ที่จะกำจัดข้อผิดพลาด ฉันเสียเวลาก่อนที่จะรู้ว่านั่นเป็นปัญหาของฉัน
Jason Ridge

สำหรับผู้ที่ต้องการแสดงภาพขนาดใหญ่ที่ยังคงรักษาคุณภาพของภาพให้อ้างอิงกับไลบรารีใน @stoefln คำตอบด้านล่าง ฉันใช้มันและคุ้มค่าที่จะลอง แน่นอนดีกว่าinSampleSizeวิธีการ
Mahendra Liya

1
@Joe Blow คำตอบปัจจุบันไม่ทำงานในกรณีของคุณ? ถ้าไม่ใช่โปรดอธิบายรายละเอียดเกี่ยวกับปัญหาที่คุณเผชิญในบริบทของคำถามนี้หรือไม่
Pravin Divraniya

1
hey @ VC.One - เป็นเรื่องแปลกที่จะคิดเกี่ยวกับผู้ชายของฉัน ฉันควรคลิก "รางวัลคำตอบที่มีอยู่" ไม่มีใครมายุ่งกับการกดปุ่ม "ดีที่สุด" ในป๊อปอัพ SO นั้นเพราะมันโง่ :) ทุกอย่างได้รับการแก้ไขแล้วเมื่อฉันคลิกความโปรดปราน ไชโย !!
Fattie

1
ฉันพยายามจ่ายเงินรางวัลเสมอ (ฉันไม่ชอบรวบรวมคะแนน) ฉันเดาว่าถ้าคุณคลิกที่โปรไฟล์ของฉันจากนั้นเงินรางวัล @ VC.One คุณจะเห็น QA ที่ยอดเยี่ยมจริงๆจากหลายปีที่ผ่านมา !!!!!!!!!!!!!!!!!
Fattie

คำตอบ:


48

การแสดงผลทั้งหมดขึ้นอยู่กับ OpenGL ดังนั้นคุณไม่สามารถข้ามขีด จำกัด นี้ได้ ( GL_MAX_TEXTURE_SIZEขึ้นอยู่กับอุปกรณ์ แต่ขั้นต่ำคือ 2048x2048 ดังนั้นรูปภาพใดก็ตามที่ต่ำกว่า 2048x2048 จะพอดี)

ด้วยภาพขนาดใหญ่เช่นนี้หากคุณต้องการซูมเข้าและในมือถือคุณควรตั้งค่าระบบให้คล้ายกับสิ่งที่คุณเห็นใน Google แผนที่ ด้วยการแบ่งภาพออกเป็นหลาย ๆ ชิ้นและหลายคำจำกัดความ

หรือคุณสามารถย่อขนาดรูปภาพก่อนที่จะแสดง (ดูคำตอบของผู้ใช้ 1352407สำหรับคำถามนี้)

และระวังด้วยว่าโฟลเดอร์ใดที่คุณใส่รูปภาพลงไป Android สามารถปรับขนาดรูปภาพโดยอัตโนมัติ ดูคำตอบของ Pilot_51ด้านล่างสำหรับคำถามนี้


23
หากเป็นกรณีนี้แอปคลังภาพจะอนุญาตให้แสดงและจัดการภาพที่ถ่ายด้วยกล้องได้อย่างไร 2048x2048 เป็นเพียงภาพขนาด 4MP และโทรศัพท์ Android จำนวนมากถ่ายภาพที่มีขนาดใหญ่กว่านี้มากและดูเหมือนว่าแอพ Gallery จะไม่มีปัญหา
Ollie C

3
เนื่องจาก GL_MAX_TEXTURE_SIZE ขึ้นอยู่กับอุปกรณ์
jptsetung

24
เรื่องนี้ไม่สมเหตุสมผลเลย ฉันพบปัญหาเดียวกันตอนนี้ - ด้วยภาพขนาด 1286x835 พิกเซล และ: เฉพาะใน Galaxy Nexus ฉันได้รับข้อความแสดงข้อผิดพลาดนี้และไม่มีภาพ! ดูเหมือนว่าไร้สาระที่สมาร์ทโฟนที่ทันสมัยไม่สามารถแสดงภาพขนาดเล็กได้! HTC Hero ของฉันสามารถแสดงสิ่งนั้นได้! ฉันจะทำอย่างไร
Zordid

1
ดูคำตอบของ Romain ได้ที่นี่: stackoverflow.com/questions/7428996/…
Ben Lee

3
@OllieC ฉันต้องการทราบว่าแอปแกลเลอรีทำงานอย่างไร ดังนั้นหากใครรู้หรือมีตัวอย่างในการแสดงภาพขนาดใหญ่นั่นจะดีมาก
Innova

408

นี่ไม่ใช่คำตอบสำหรับคำถามโดยตรง (โหลดรูปภาพ> 2048) แต่เป็นวิธีการแก้ไขที่เป็นไปได้สำหรับทุกคนที่พบข้อผิดพลาด

ในกรณีของฉันรูปภาพมีขนาดเล็กกว่า 2048 ทั้งสองมิติ (1280x727 เป็นที่แน่นอน) และปัญหานี้เกิดขึ้นกับ Galaxy Nexus เป็นพิเศษ รูปภาพอยู่ในdrawableโฟลเดอร์และไม่มีโฟลเดอร์ที่ผ่านการรับรอง Android สมมติว่า drawables ที่ไม่มีตัวระบุความหนาแน่นคือ mdpi และปรับขนาดขึ้นหรือลงสำหรับความหนาแน่นอื่น ๆ ในกรณีนี้ขยายขนาด 2x สำหรับ xhdpi การย้ายอิมเมจผู้ร้ายไปdrawable-nodpiยังเพื่อป้องกันการปรับสเกลแก้ไขปัญหาได้


3
ฉันมีปัญหาหน่วยความจำที่พยายามโหลด drawables ใช้เวลา 2 ชั่วโมงพยายามที่จะเข้าใจว่าทำไมสิ่งนี้จึงเกิดขึ้น ขอบคุณสำหรับคำตอบทางอ้อม
TrueCoke

16
นี่คือคำตอบที่ถูกต้องและควรทำเครื่องหมายเช่นนี้
wblaschko

3
ประมาณสามเดือนครึ่งตอนนี้ฉันได้เขียนโปรแกรมเกือบเต็มเวลาบน Android และตอนนี้ฉันเพิ่งรู้สิ่งนี้ ดูเหมือนว่าใบ้จะสร้างโฟลเดอร์ที่drawableซ่อนอยู่ drawable-mdpiสิ่งนี้ยังอธิบายว่าทำไมเครื่องหมายแผนที่ที่กำหนดเองของฉันดูแย่มาก (พวกเขาถูกลดขนาดแล้วลดขนาด)
theblang

10
ใช่สิ่งที่นรก! ฉันคิดว่า 99% ของโปรแกรมเมอร์ Android คิดว่า "drawable" หมายถึง "อย่าขยายขนาดนี้"
แมตต์โลแกน

3
นี่คือคำตอบที่เป็นประโยชน์ที่สุดที่ฉันเคยเห็นทุกที่ ทั้งหมดที่ฉันสามารถพูดได้ก็คือฉันส่งเงินรางวัล! ขอบคุณ
Fattie

105

ฉันย่อขนาดรูปภาพด้วยวิธีนี้:

ImageView iv  = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);

2
ขอบคุณ createScaledBitmap เป็นประโยชน์สำหรับฉันที่จะสามารถแสดงบิตแมปที่มีขนาดใหญ่เกินไป
บอย

แก้ไขปัญหาของฉัน .. ขอบคุณ จริงๆแล้วฉันกำลังถ่ายรูปจากกล้องและแสดงใน ImageView ใน Samsung Galaxy S4 ด้วย 4.4.2
Mitesh Shah

ขอโทษด้วย แต่ฉันไม่สามารถตรวจสอบได้ว่า "w" คืออะไร
Bobbelinio

1
ขออภัยนี่ctxหมายถึงอะไร
Ameer Sabith

1
@AmeerSabith ดูเหมือนว่า ctx เป็นวัตถุบริบท (เช่นกิจกรรมที่คุณใช้)
แมตต์

34

แทนการใช้จ่ายชั่วโมงต่อชั่วโมงพยายามที่จะเขียน / แก้ปัญหาโค้ดทั้งหมดลดขนาดนี้ด้วยตนเองทำไมไม่ใช้Picasso? มันถูกสร้างขึ้นเพื่อจัดการกับbitmapsทุกประเภทและ / หรือขนาด

ฉันใช้รหัสบรรทัดเดียวนี้เพื่อลบปัญหา"บิตแมปที่มีขนาดใหญ่เกินไป .... " :

Picasso.load(resourceId).fit().centerCrop().into(imageView);

การใช้ centerCrop () โดยไม่เรียกการปรับขนาด () จะส่งผลให้ IllegalStateException ไลบรารีบังคับให้เรียกการปรับขนาดเมื่อใช้การครอบตัดกึ่งกลาง
Zsolt Boldizsár

นั่นทำให้รู้สึกที่สมบูรณ์แบบเนื่องจากการครอบตัดแสดงว่าคุณกำลังปรับขนาดรูปภาพ
Phileo99

2
วิธีแก้ปัญหาที่รวดเร็วง่ายและเรียบง่าย ไม่แน่ใจว่าดีที่สุดหรือไม่ แต่ฉันได้รับสิ่งที่ฉันต้องการ ขอบคุณมาก
Nabin

ยังคงได้รับข้อผิดพลาดเดียวกันเมื่อใช้เป้าหมายกับ Picasso สำหรับภาพขนาดใหญ่ :(
Narendra Singh

ลองใช้รุ่น 2.5.3-SNAPSHOT ดูเหมือนว่าตอนนี้ทำงานได้อย่างถูกต้องกับภาพใหญ่
Anton Malmygin

20

นอกเหนือจาก 2 คุณลักษณะต่อไปนี้ใน ( AndroidManifest.xml) ได้ผลสำหรับฉัน:

android:largeHeap="true"
android:hardwareAccelerated="false"

สิ่งนี้แก้ไขปัญหาของฉันบนอุปกรณ์ Samsung ขอบคุณ
Hitesh Bisht

16

การเปลี่ยนไฟล์ภาพไปยังdrawable-nodpiโฟลเดอร์จากdrawableโฟลเดอร์ทำงานได้สำหรับฉัน


สิ่งนี้จะป้องกันไม่ให้ Android พยายามปรับขนาดภาพโดยอัตโนมัติตามขนาดของอุปกรณ์และความหนาแน่นของหน้าจอ นี่คือสาเหตุที่โซลูชันนี้ใช้งานได้ Android จะพยายามปรับขนาดอัตโนมัติในdrawableโฟลเดอร์
Chris Cirefice

10

ฉันใช้ Picasso และมีปัญหาเดียวกัน รูปภาพมีขนาดใหญ่เกินไปอย่างน้อยก็ในขนาดความกว้างหรือความสูง ในที่สุดฉันก็พบทางออกที่นี่ คุณสามารถลดขนาดภาพใหญ่ตามขนาดการแสดงผลและรักษาอัตราส่วน:

    public Point getDisplaySize(Display display) {
    Point size = new Point();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        display.getSize(size);
    } else {
        int width = display.getWidth();
        int height = display.getHeight();
        size = new Point(width, height);
    }

    return size;
}

และใช้วิธีนี้ในการโหลดภาพโดย Picasso:

    final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
        final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
        Picasso.with(this)
                .load(urlSource)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

เพื่อประสิทธิภาพที่ดียิ่งขึ้นคุณสามารถดาวน์โหลดภาพตามความกว้างและความสูงของหน้าจอแสดงผลได้ไม่รวมภาพทั้งหมด:

    public String reviseImageUrl(final Integer displayWidth,     final Integer displayHeight,
        final String originalImageUrl) {
    final String revisedImageUrl;

    if (displayWidth == null && displayHeight == null) {
        revisedImageUrl = originalImageUrl;
    } else {
        final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();

        if (displayWidth != null && displayWidth > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
        }

        if (displayHeight != null && displayHeight > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
        }

        revisedImageUrl = uriBuilder.toString();
    }

    return revisedImageUrl;
}

    final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);

แล้ว:

    Picasso.with(this)
                .load(newImageUlr)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

แก้ไข: getDisplaySize ()

display.getWidth()/getHeight()เลิกใช้แล้ว แทนการใช้งานDisplayDisplayMetrics

public Point getDisplaySize(DisplayMetrics displayMetrics) {
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        return new Point(width, height);
}

6

BitmapRegionDecoder ไม่หลอกลวง

คุณสามารถแทนที่onDraw(Canvas canvas)เริ่มเธรดใหม่และถอดรหัสพื้นที่ที่ผู้ใช้มองเห็นได้


5

ตามที่ Larcho ชี้โดยเริ่มต้นจาก API ระดับ 10 คุณสามารถใช้BitmapRegionDecoderเพื่อโหลดภูมิภาคเฉพาะจากรูปภาพและด้วยเหตุนี้คุณจึงสามารถแสดงภาพขนาดใหญ่ด้วยความละเอียดสูงได้โดยการจัดสรรในหน่วยความจำเฉพาะภูมิภาคที่ต้องการ ฉันเพิ่งพัฒนา lib ที่ให้ภาพของภาพขนาดใหญ่ที่มีการจัดการท่าทางสัมผัส รหัสแหล่งที่มาและกลุ่มตัวอย่างที่มีอยู่ที่นี่


3

ดูระดับ

คุณสามารถปิดการใช้งานการเร่งด้วยฮาร์ดแวร์สำหรับมุมมองส่วนบุคคลตอนรันไทม์ด้วยรหัสต่อไปนี้:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, null);


3

ฉันพบปัญหาเดียวกันนี่คือวิธีแก้ปัญหาของฉัน ตั้งค่าความกว้างของภาพเช่นเดียวกับความกว้างของหน้าจอ Android จากนั้นปรับขนาดความสูง

Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);

นี่จะดีกว่าสำหรับหน้าจอ Android ทุกขนาด แจ้งให้เราทราบหากมันเหมาะกับคุณ


2

ย่อขนาดรูปภาพ:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);

int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
    pow += 1;
options.inSampleSize = 1 << pow; 
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);

รูปภาพจะถูกลดขนาดลงตามขนาดของ reqHeight และ reqWidth ดังที่ฉันเข้าใจ inSampleSize ใช้เพียงค่าพลัง 2 ค่า


จะทราบ reqHeight และ reqWidth ได้อย่างไร ฉันหมายถึงเราจำเป็นต้องแก้ไขให้เป็นค่าคงที่? หรือเราสามารถเปลี่ยนอุปกรณ์ wrt เหล่านั้นได้?
Prashanth Debbadwar

2

ใช้ไลบรารี Glide แทนการโหลดเข้าสู่ imageview โดยตรง

ร่อน: https://github.com/bumptech/glide

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);

2
การใช้ Glide แทน Picasso ช่วยได้ ดูเหมือนว่า Glide จะจัดการกับปัญหาเหล่านี้โดยปริยาย
Johnny Five

1

ฉันลองใช้วิธีแก้ปัญหาทั้งหมดข้างต้นเป็นระยะเวลานานหลายชั่วโมงและดูเหมือนจะไม่มีใครทำงาน! ในที่สุดฉันตัดสินใจที่จะดูตัวอย่างเป็นทางการเกี่ยวกับการถ่ายภาพด้วยกล้องของ Android และแสดงมัน ตัวอย่างเป็นทางการ ( ที่นี่ ) ในที่สุดก็ให้วิธีการเดียวที่ใช้ได้ ด้านล่างฉันนำเสนอวิธีแก้ปัญหาที่ฉันพบในแอพตัวอย่างนั้น:

public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {

            /* There isn't enough memory to open up more than a couple camera photos */
    /* So pre-scale the target bitmap into which the file is decoded */

    /* Get the size of the ImageView */
    int targetW = imgView.getWidth();
    int targetH = imgView.getHeight();

    /* Get the size of the image */
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    /* Figure out which way needs to be reduced less */
    int scaleFactor = 1;
    if ((targetW > 0) || (targetH > 0)) {
        scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    }

    /* Set bitmap options to scale the image decode target */
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    /* Decode the JPEG file into a Bitmap */
    Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);

    /* Associate the Bitmap to the ImageView */
    imgView.setImageBitmap(bitmap);
    imgView.setVisibility(View.VISIBLE);
}

0

หมายเหตุสำหรับผู้ที่ต้องการใส่ภาพขนาดเล็ก:

วิธีการแก้ปัญหาของ Pilot_51 (การย้ายรูปภาพไปยังdrawable-nodpiโฟลเดอร์) ทำงานได้ แต่มีปัญหาอื่น: ทำให้รูปภาพเล็กเกินไปบนหน้าจอเว้นแต่ภาพจะถูกปรับขนาดให้มีความละเอียดมาก (เช่น 2000 x 3800) ให้พอดีกับหน้าจอ - จากนั้นทำให้แอปของคุณหนักขึ้น .

แนวทางแก้ไข : ใส่ไฟล์ภาพของคุณในdrawable-hdpi- มันทำงานได้ดีเหมือนมีเสน่ห์สำหรับฉัน


1
คุณกำลังปกปิดปัญหาความหนาแน่นของภาพ ในฮาร์ดแวร์ที่มีความหนาแน่นต่ำภาพของคุณจะดูใหญ่ขึ้น
rupps

ฉันยังไม่คิดว่าวิธีการแก้ปัญหาของ Pilot_51 มีปัญหาใด ๆ คุณควรใช้ขนาดภาพที่เหมาะสมตามความต้องการของคุณ
Nilabja

0

การใช้โฟลเดอร์ย่อยdrawable ที่ถูกต้องแก้ไขได้สำหรับฉัน ทางออกของฉันคือการใส่ภาพความละเอียดเต็ม (1920x1200) ลงในโฟลเดอร์drawable-xhdpiแทนที่จะเป็นโฟลเดอร์ drawable

ฉันยังใส่ภาพขนาดย่อลง (1280x800) ลงในโฟลเดอร์drawable-hdpi

ความละเอียดทั้งสองนี้ตรงกับแท็บเล็ต Nexus 7 2013 และ 2012 ที่ฉันกำลังเขียนโปรแกรม ฉันยังทดสอบวิธีแก้ปัญหาบนแท็บเล็ตอื่น ๆ


0
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);
    ///*
    if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){



        uri = data.getData();

        String[] prjection ={MediaStore.Images.Media.DATA};

        Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);

        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(prjection[0]);

        ImagePath = cursor.getString(columnIndex);

        cursor.close();

        FixBitmap = BitmapFactory.decodeFile(ImagePath);

        ShowSelectedImage = (ImageView)findViewById(R.id.imageView);

      //  FixBitmap = new BitmapDrawable(ImagePath);
        int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
        FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);

       // ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));

        ShowSelectedImage.setImageBitmap(FixBitmap);

    }
}

รหัสนี้ใช้งานได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.