Zła jakość zdjęcia po zmniejszaniu przez Graphics2D.scale

Odpowiedz Nowy wątek
2011-09-28 08:42
0

Witam,

mam problem ze skalowaniem obrazka. Używam do tego celu Graphics2D.scale, ale napis po zmniejszeniu staje się mało czytelny. Powinienem skalować to w inny sposób?

Załączam kod i obrazek, by zilustrować problem.

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Skalowanie {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable(){
            public void run(){
                JFrame frame=new JFrame();
                frame.add(new Obrazek());
                frame.setSize(300, 200);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

   class Obrazek extends JPanel{
        Obrazek(){
            try{
                obraz=ImageIO.read(new File("napis.png"));
            }catch(IOException e){
                e.printStackTrace();
            }
        }
        public void paintComponent(Graphics g){
            super.paintComponent(g);

            if(obraz==null) return;
            Graphics2D g2=(Graphics2D)g;
            g2.scale(1./7, 1./7);

            g2.drawImage(obraz, 0, 0, this);
        }
        private Image obraz;
    };

proszę Was o pomoc i pozdrawiam
Tomek

Pozostało 580 znaków

2011-09-28 13:19
0

do przeskalowania obrazka można użyć Image.getScaledInstance(int width, int height, int hints). Niestety trwa to zdecydowanie za długo:(

edytowany 2x, ostatnio: Tompey, 2011-09-28 21:13
Mi się wydaje że wystarczyło podać jakieś Rendering hints, zresztą jest to w metodzie getScaledInstance(). - szypxx 2011-09-28 14:24

Pozostało 580 znaków

2011-09-28 21:12
0

Dziękuję za podpowiedź szypxx.
Nie znałem setRenderingHints a faktycznie ustawienie tych wskazówek poprawia jakość. Wprawdzie jakość jest dużo słabsza niż przez getScaledInstance i dużym zmiejszeniu dalej napisy robią się mało czytelne, ale za to tworzy się to w przyzwoitym czasie, a na getScaledInstance czeka się i czeka.

Pokombinuję z parametrami pliku graficznego, może uda się poprawić wygląd zmieniając dpi, albo rozdzielczość tego pliku. Plik chciałem przechowywać w formacie do wydruku A4, ale zależy mi też żeby można byłogo zmniejszyć i czytać napisy obrazka na ekranie. Pewnie jest jakiś sposób, bo programy typu irfanView zmiejszają ten plik bardzo szybko i wszystko jest czytelne. Może należy zmiejszać o jakąś konkretną wartość by operacje były szybkie?

Pozostało 580 znaków

2011-09-29 16:06
0

Poniżej masz przykład ulepszonego skalowania, który wykorzystuje technikę podziału downscalingu na mniejsze kroki (każdy krok dzieli przez dwa). To skalowanie powstało na podstawie dostępnych w necie pomysłów i algorytmów. Zmniejszone detale takie jak tekst są zwykle widoczne jeżeli mają prawo być widoczne. :)
Kod można sobie wykorzystać lub przerobić.

package com.olamagato.swing;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ConvolveOp;
import java.awt.image.IndexColorModel;
import java.awt.image.Kernel;
import java.awt.image.WritableRaster;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Klasa opakowująca BufferedImage mająca zmodyfikowane skalowanie
 * @author Olamagato
 * @version 2.1
 */
public class Image2D
{   //ustawienia dla skalowania
    public enum Config { BEST_QUALITY, UNCHANGED_COLORS; }

    /**
     *
     * @param width
     * @param height
     * @param imageType
     */
    public Image2D(int width, int height, int imageType)
    {
        buf = new BufferedImage(width, height, imageType);
    }

    /**
     *
     * @param width
     * @param height
     * @param imageType
     * @param cm
     */
    public Image2D(int width, int height, int imageType, IndexColorModel cm)
    {
        buf = new BufferedImage(width, height, imageType, cm);
    }

    /**
     *
     * @param <K> typ klucza
     * @param <V> typ wartości
     * @param cm model koloru
     * @param raster typ rastra
     * @param isRasterPremultiplied flaga rastra
     * @param properties ustawienia BufferedImage
     */
    public <K,V> Image2D(ColorModel cm, WritableRaster raster,
        boolean isRasterPremultiplied, Map<K,V> properties)
    {
        @SuppressWarnings("UseOfObsoleteCollectionType")
        final java.util.Hashtable<K,V> ht =
            new java.util.Hashtable<>(properties);
        buf = new BufferedImage(cm, raster, isRasterPremultiplied, ht);
    }

    /**
     * Opakowuje istniejący obiekt Buffered Image
     * @param image opakowywany obraz
     */
    public Image2D(BufferedImage image) { buf = image; }

    /**
     * Opakowuje istniejący obiekt przez skopiowanie jego obiektu obrazu
     * @param image opakowywany obraz
     */
    public Image2D(Image2D image) { buf = image.getBufferedImage(); }

    /**
     * Skaluje obraz do podanego wymiaru
     * @param newSize rozmiar nowego obrazka
     * @param typSkalowania rodzaj skalowania
     * @return nowy wyskalowany do wymiaru obraz
     */
    public Image2D scale(Dimension newSize, final Config typSkalowania)
    {
        if(typSkalowania == Config.BEST_QUALITY)
            return scale(newSize.width, newSize.height,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);
        else if(typSkalowania == Config.UNCHANGED_COLORS)
        {
            Image2D roboczy = new Image2D(GUI.getDefGraph()
                .createCompatibleImage(newSize.width, newSize.height,
                Transparency.TRANSLUCENT));
            Graphics2D g = roboczy.getBufferedImage().createGraphics();
            g.setRenderingHints(niezmienne_kolory);
            g.drawImage(roboczy.getBufferedImage(), 0, 0,
                newSize.width, newSize.height, null);
            g.dispose();
            return roboczy;
        }
        return null;
    }

    /**
     * Better up/down scaling.
     *
     * @param width new width
     * @param height new height
     * @param hint BufferedImage constans:
     * 1. SCALE_DEFAULT, SCALE_FAST,
     * 2. SCALE_SMOOTH,
     * 3. SCALE_REPLICATE, SCALE_AREA_AVERAGING
     *
     * @return rescaled image
     */
    public Image2D scale(final int width, final int height, final int hint)
    {
        Object h = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
        Boolean q = false;
        switch(hint)
        {
            case BufferedImage.SCALE_DEFAULT: case BufferedImage.SCALE_FAST:
                break;
            case BufferedImage.SCALE_SMOOTH:
                q = true;
                h = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
                break;
            case BufferedImage.SCALE_REPLICATE:
            case BufferedImage.SCALE_AREA_AVERAGING:
                h = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
        }
        return scale(width, height, h, q);
    }

    /**
     * Use multi-step technique: start with original size, then
     * scale down in multiple passes with drawImage()
     * until the target size is reached
     * @param width the desired width of the scaled instance,
     *    in pixels
     * @param height the desired height of the scaled instance,
     *    in pixels
     * @param interpolation one of the rendering hints that corresponds to
     *    {@code RenderingHints.KEY_INTERPOLATION} (e.g.
     *    {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
     *    {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
     *    {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
     * @param highQuality if true, this method will use a multi-step
     *    scaling technique that provides higher quality than the usual
     *    one-step technique (only useful in downscaling cases, where
     *    {@code targetWidth} or {@code targetHeight} is
     *    smaller than the original dimensions, and generally only when
     *    the {@code BILINEAR} hint is specified)
     * @return a scaled version of the original {@code Image2D}
     */
    public Image2D scale(final int width, final int height,
        Object interpolation, final boolean highQuality)
    {
        int typ = buf.getType();
        if(typ == BufferedImage.TYPE_CUSTOM)
            typ = (buf.getTransparency() == Transparency.OPAQUE)?
                BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        //wielostopniowy downscaling
        int w = buf.getWidth(), h = buf.getHeight();
        //jednostopniowy upscaling lub gdy bez wysokiej jakości
        if (width > w || !highQuality) w = width;
        if (height > h || !highQuality) h = height;
        BufferedImage roboczy, wynik = buf;
        if(highQuality)
        {   //wygładzenie przed zmianą rozmiaru
            roboczy = new BufferedImage(w, h, typ);
            roboczy.setAccelerationPriority(NO_ACCELERATION);
            wygładzanie.filter(buf, roboczy);
            wynik = roboczy;
        }

        do //pętla dla wielostopniowego skalowania
        {   //dwukrotne zmniejszenie wymiarów w przypadku downscalingu
            if(w > width && (w >>>= 1) < width ) //uboczne przypisania!
                w = width;
            if(h > height && (h >>>= 1) < height ) //uboczne przypisania!
                h = height;
            roboczy = new BufferedImage(w, h, typ);
            roboczy.setAccelerationPriority(NO_ACCELERATION);
            Graphics2D g2 = roboczy.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                interpolation);
            g2.drawImage(wynik, 0, 0, w, h, null);
            g2.dispose();
            wynik = roboczy;
        }
        while(w != width || h != height);
        return new Image2D(wynik);
    }

    private static final ConvolveOp wygładzanie =
        new ConvolveOp(new Kernel(3, 3, wypełnij(new float[9], 1/9f)),
        ConvolveOp.EDGE_NO_OP, null);

    private static float[] wypełnij(float[] f, float liczba)
        { Arrays.fill(f, liczba); return f; }

    private static final
        RenderingHints niezmienne_kolory = niezmienne_kolory_init();

    private static RenderingHints niezmienne_kolory_init()
    {   //ustawić wszystkie potrzebne wartości RenderingHints
        RenderingHints.Key[] k =
        {
            RenderingHints.KEY_INTERPOLATION,
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.KEY_COLOR_RENDERING,
            RenderingHints.KEY_DITHERING,
            RenderingHints.KEY_STROKE_CONTROL,
            RenderingHints.KEY_RENDERING,
            RenderingHints.KEY_ALPHA_INTERPOLATION,
        };
        Object [] v =
        {
            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR,
            RenderingHints.VALUE_ANTIALIAS_OFF,
            RenderingHints.VALUE_COLOR_RENDER_SPEED,
            RenderingHints.VALUE_DITHER_DISABLE,
            RenderingHints.VALUE_STROKE_PURE,
            RenderingHints.VALUE_RENDER_SPEED,
            RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED,
        };
        HashMap<RenderingHints.Key,Object> mapa = new HashMap<>();
        for(int i = 0; i < k.length; ++i)
            mapa.put(k[i],v[i]);
        return new RenderingHints(mapa);
    }

    /**
     * @return Wrapped Image
     */
    public BufferedImage getBufferedImage() { return buf; }

    private static final float NO_ACCELERATION = 0f;

    private BufferedImage buf;
}

Jeżeli ktoś komuś coś, ewentualnie nikt nikomu nic, to właściwie po co...?

Pozostało 580 znaków

2011-09-30 10:58
0
Olamagato napisał(a)

Poniżej masz przykład ulepszonego skalowania...

Dziękuję:) przeanalizuję ten kod

Dziękuję i pozdrawiam

Pierwsze dwie metody scale istnieją dla zobrazowania możliwości trzeciej oraz do upodobnienia metod do tych dostępnych w klasach bibliotecznych takich jak BufferedImage. Dla tej ostatniej ta klasa jest wrapperem bo ma niemal identyczny interfejs (więc i używa się jej identycznie). Trzecia metoda nie musi używać najwolniejszej interpolacji VALUE_INTERPOLATION_BICUBIC, która jest użyta w pierwszej scale(), można użyć innej, szybszej interpolacji. - Olamagato 2011-09-30 12:43

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0