java - BufferedImage को कैसे स्केल करें




4 Answers

AffineTransformOp इंटरपोलेशन प्रकार चुनने की अतिरिक्त लचीलापन प्रदान करता है।

BufferedImage before = getBufferedImage(encoded);
int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(2.0, 2.0);
AffineTransformOp scaleOp = 
   new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);

दिखाया गया टुकड़ा resampling दिखाता है, cropping नहीं; यह संबंधित answer इस issue संबोधित करता issue ; यहां कुछ संबंधित उदाहरणों की जांच की जाती here ।

Javadocs के बाद, मैंने सफलता के बिना BufferedImage को स्केल करने की कोशिश की है मेरा कोड है:

BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded);
Graphics2D grph = image.createGraphics();
grph.scale(2.0, 2.0);
grph.dispose();

मैं समझ नहीं पा रहा हूं कि यह क्यों काम नहीं कर रहा है, कोई मदद?




@ बोझो कहते हैं, आप शायद getScaledInstance का उपयोग करना चाहते हैं।

यह समझने के लिए कि grph.scale(2.0, 2.0) कैसे काम करता है, आप इस कोड को देख सकते हैं:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;


class Main {
    public static void main(String[] args) throws IOException {

        final int SCALE = 2;

        Image img = new ImageIcon("duke.png").getImage();

        BufferedImage bi = new BufferedImage(SCALE * img.getWidth(null),
                                             SCALE * img.getHeight(null),
                                             BufferedImage.TYPE_INT_ARGB);

        Graphics2D grph = (Graphics2D) bi.getGraphics();
        grph.scale(SCALE, SCALE);

        // everything drawn with grph from now on will get scaled.

        grph.drawImage(img, 0, 0, null);
        grph.dispose();

        ImageIO.write(bi, "png", new File("duke_double_size.png"));
    }
}

दिया गया duke.png :

यह duke_double_size.png का उत्पादन करता है:




यदि आपको बाहरी पुस्तकालय का उपयोग करने में कोई फर्क नहीं पड़ता है, तो BufferedImage s के स्केलिंग कर सकता है।

थंबनेलेटर जावा 2 डी प्रसंस्करण (जैसे Graphics2D 2 डी का उपयोग करना और उचित प्रतिपादन संकेतों को सेट करना) को संभालने का ख्याल रखेगा ताकि छवियों का आकार बदलने के लिए एक साधारण धाराप्रवाह API कॉल का उपयोग किया जा सके:

BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();

यद्यपि थंबनेलेटर, जैसा कि इसके नाम से तात्पर्य है, छवियों को कम करने की दिशा में तैयार है, यह अपने डिफ़ॉल्ट पुनर्विक्रेता कार्यान्वयन में बिलीनेर इंटरपोलेशन का उपयोग करके छवियों को बढ़ाना एक सभ्य काम करेगा।

अस्वीकरण: मैं Thumbnailator लाइब्रेरी का रखरखाव हूं।




एक छवि को स्केल करने के लिए, आपको एक नई छवि बनाने और इसमें आकर्षित करने की आवश्यकता है। एक तरीका है जैसा कि here AffineTransferOp , एक AffineTransferOp की filter() विधि का उपयोग करना here । यह आपको इंटरपोलेशन तकनीक चुनने की अनुमति देता है।

private static BufferedImage scale1(BufferedImage before, double scale) {
    int w = before.getWidth();
    int h = before.getHeight();
    // Create a new image of the proper size
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp 
        = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);

    scaleOp.filter(before, after);
    return after;
}

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

private static BufferedImage scale2(BufferedImage before, double scale) {
    int w = before.getWidth();
    int h = before.getHeight();
    // Create a new image of the proper size
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB);
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp 
        = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);

    Graphics2D g2 = (Graphics2D) after.getGraphics();
    // Here, you may draw anything you want into the new image, but we're
    // drawing a scaled version of the original image.
    g2.drawImage(before, scaleOp, 0, 0);
    g2.dispose();
    return after;
}

परिशिष्ट: परिणाम

मतभेदों को स्पष्ट करने के लिए, मैंने नीचे दी गई पांच विधियों के परिणामों की तुलना की। निष्पादन डेटा के साथ, परिणाम ऊपर और नीचे दोनों को स्केल किए गए हैं। (प्रदर्शन एक रन से अगले तक भिन्न होता है, इसलिए इन नंबरों को केवल किसी न किसी दिशानिर्देश के रूप में लें।) शीर्ष छवि मूल है। मैं इसे डबल आकार और आधा आकार स्केल करता हूं।

जैसा कि आप देख सकते हैं, AffineTransformOp.filter() , AffineTransformOp.filter() में उपयोग किया जाता है, scaleBilinear() में मानक 2 Graphics2D.drawImage() में मानक ड्राइंग विधि से तेज़ है। इसके अलावा बायक्यूबिक इंटरपोलेशन सबसे धीमा है, लेकिन छवि का विस्तार करते समय सर्वोत्तम परिणाम देता है। (प्रदर्शन के लिए, इसकी तुलना केवल scaleBilinear() और scaleNearest(). साथ की जानी चाहिए scaleNearest(). ) scaleNearest(). छवि को कम करने के लिए बेहतर प्रतीत होता है, हालांकि यह एक कठिन कॉल है। और सबसे नजदीकी नजदीक सबसे नज़दीकी परिणाम है। बिलिनर गति और गुणवत्ता के बीच सबसे अच्छा समझौता प्रतीत होता है। Image.getScaledInstance() , जिसे questionable() विधि में बुलाया गया है, बहुत खराब प्रदर्शन करता है, और निकटतम नेबेबर के समान गुणवत्ता को वापस कर देता है। (प्रदर्शन संख्या केवल छवि का विस्तार करने के लिए दी जाती है।)

public static BufferedImage scaleBilinear(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_BILINEAR;
    return scale(before, scale, interpolation);
}

public static BufferedImage scaleBicubic(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_BICUBIC;
    return scale(before, scale, interpolation);
}

public static BufferedImage scaleNearest(BufferedImage before, double scale) {
    final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
    return scale(before, scale, interpolation);
}

@NotNull
private static 
BufferedImage scale(final BufferedImage before, final double scale, final int type) {
    int w = before.getWidth();
    int h = before.getHeight();
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, before.getType());
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type);
    scaleOp.filter(before, after);
    return after;
}

/**
 * This is a more generic solution. It produces the same result, but it shows how you 
 * can draw anything you want into the newly created image. It's slower 
 * than scaleBilinear().
 * @param before The original image
 * @param scale The scale factor
 * @return A scaled version of the original image
 */
private static BufferedImage scale2(BufferedImage before, double scale) {
    int w = before.getWidth();
    int h = before.getHeight();
    // Create a new image of the proper size
    int w2 = (int) (w * scale);
    int h2 = (int) (h * scale);
    BufferedImage after = new BufferedImage(w2, h2, before.getType());
    AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale);
    AffineTransformOp scaleOp
            = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR);

    Graphics2D g2 = (Graphics2D) after.getGraphics();
    // Here, you may draw anything you want into the new image, but we're just drawing
    // a scaled version of the original image. This is slower than 
    // calling scaleOp.filter().
    g2.drawImage(before, scaleOp, 0, 0);
    g2.dispose();
    return after;
}

/**
 * I call this one "questionable" because it uses the questionable getScaledImage() 
 * method. This method is no longer favored because it's slow, as my tests confirm.
 * @param before The original image
 * @param scale The scale factor
 * @return The scaled image.
 */
private static Image questionable(final BufferedImage before, double scale) {
    int w2 = (int) (before.getWidth() * scale);
    int h2 = (int) (before.getHeight() * scale);
    return before.getScaledInstance(w2, h2, Image.SCALE_FAST);
}



Related