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

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

0

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

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?

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;
}
0
Olamagato napisał(a)

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

Dziękuję:) przeanalizuję ten kod

Dziękuję i pozdrawiam

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