2009-06-13 21 views
16

im próby zmiany rozmiaru bufferdImage w pamięci w Javie, ale aby utrzymać współczynnik kształtu obrazu im coś takiego, ale to nie jest dobreResize obrazu przy zachowaniu proporcji w Javie

int w = picture.getWidth(); 
int h = picture.getWidth(); 
int neww=w; 
int newh=h; 
int wfactor = w; 
int hfactor = h; 
if(w > DEFULT_PICTURE_WIDTH || h > DEFULT_PICTURE_HIGHT) 
{ 
    while(neww > DEFULT_PICTURE_WIDTH) 
    { 
     neww = wfactor /2; 
     newh = hfactor /2; 
     wfactor = neww; 
     hfactor = newh; 
    } 
} 

picture = Utils.resizePicture(picture,neww,newh); 

Odpowiedz

8

Na początek - spójrz na linię 2. Nie powinno to być getHeight()?

Nie chcesz mieć pętli do zmiany rozmiaru, chcesz sprawdzić współczynnik zmiany rozmiaru, który jest prostą częścią matematyki.

(width/height) = (new_width/new_height) 

Jeśli wiesz, jeden z „nowych” rozmiarach, drugi można znaleźć poprzez pomnożenie

new_height * (width/height) = new_width 

Można również użyć metody leniwy świadczone przez BufferedImage's nadklasą Obraz, getScaledInstance() - używając -1 dla obu szerokości lub wysokości utrzyma proporcji

np
scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);

+0

tak źle jej getHeight literówka jest jakiś powód, dla którego nie mogę używać getScaledInstance z BufferdImage? – user63898

+0

To powinno być w porządku, ponieważ BufferedImage rozszerza Image – Max

+0

Hah, dziękuję za to pierwsze równanie. Zrobiłem coś, co kliknę w mój zdezorientowany mózg! – Cameron

0

Aby zmienić rozmiar obrazu w0 x h0 na w1 x h1, zachowując proporcje, a następnie oblicz skalę pionową i poziomą i wybierz mniejszą.

double scalex = 1; 
double scaley = 1; 
if (scalingMode == ScalingMode.WINDOW_SIZE) { 
    scalex = (double)getWidth()/frontbuffer.getWidth(); 
    scaley = (double)getHeight()/frontbuffer.getHeight(); 
} else 
if (scalingMode == ScalingMode.KEEP_ASPECT) { 
    double sx = (double)getWidth()/frontbuffer.getWidth(); 
    double sy = (double)getHeight()/frontbuffer.getHeight(); 
    scalex = Math.min(sx, sy); 
    scaley = scalex; 
    // center the image 
    g2.translate((getWidth() - (frontbuffer.getWidth() * scalex))/2, 
    (getHeight() - (frontbuffer.getHeight() * scaley))/2); 
} 
g2.scale(scalex, scaley); 
if (interpolation != ImageInterpolation.NONE) { 
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation.hint); 
} 
g2.drawImage(frontbuffer, 0, 0, null); 
+0

co reprezentuje frontbuffer? – user63898

+0

To jest fragment z jednego z moich kodów, który renderuje animację przy użyciu buforowania swap. frontbuffer to po prostu BufferedImage, który musi zostać wyświetlony w danym momencie. – akarnokd

8

Jeśli szerokość, wysokość źródła i celu są znane, należy użyć następującej funkcji do określenia skali obrazu.

private double determineImageScale(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) { 

double scalex = (double) targetWidth/sourceWidth; 
double scaley = (double) targetHeight/sourceHeight; 
return Math.min(scalex, scaley); 

}

Następnie za pomocą tej skali do skalowania w górę/w dół obrazu przy użyciu następujący kod

Image scaledImage = sourceBufferedImage.getScaledInstance((int) (width * scale), (int) (height * scale), Image.SCALE_SMOOTH); 
63

Dodanie do punktu Erika dotyczącego getScaledInstance, jeśli odszedłeś od niego do korzystania z zalecanych mechanizmów skalowania w Java2D, mogłeś zauważyć, że twoje obrazy wyglądają znacznie gorzej.

Powodem tego jest, gdy Java2D zniechęcony wykorzystanie getScaledInstance i AreaAveragingScaleFilter, nie zastąpić go czymś jako łatwego korzystać w API, zamiast zostaliśmy do naszych własnych urządzeń za pomocą Java2D API bezpośrednio. Na szczęście Chris Campbell (z zespołu J2D) podjął decyzję o zastosowaniu techniki przyrostowego skalowania, która daje podobne rezultaty do AreaAveragingScaleFilter i działa szybciej; niestety kod ma przyzwoity rozmiar i nie odnosi się do pierwotnej kwestii uhonorowania proporcji.

Około 6 miesięcy temu ciągle widziałem te wszystkie pytania na temat "skalowania obrazów w Javie" i ostatecznie zebrałem wszystkie rady, wykonałem wszystkie kopanie i badania, jakie mogłem, i skompilowałem wszystko w jeden "najlepszy" praktyki "image scaling library.

Interfejs API jest prosty, ponieważ jest to tylko jedna klasa i kilka statycznych metod.Użytkowanie podstawowe wygląda następująco:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, 320); 

To simplest call gdzie biblioteka wykorzysta najlepiej odgadnąć jakości, honorować swoje proporcje obrazu i dopasować wynik w oknie 320x320 ograniczenia. UWAGA, ramka ograniczająca jest po prostu maksymalną wartością W/H, ponieważ proporcje obrazu są honorowane, wynikowy obraz nadal będzie honorował to, powiedzmy 320x200.

Jeśli chcesz zastąpić tryb automatyczny i zmusić go do uzyskania najlepszego wyniku, a nawet zastosować bardzo łagodny filtr antyaliasingowy, aby wyglądał jeszcze lepiej (szczególnie dobrze dla miniatur), to połączenie będzie wyglądać następująco:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 
             150, 100, Scalr.OP_ANTIALIAS); 

Są to tylko przykłady, API jest szeroki i obejmuje wszystko, od bardzo prostych przypadków użycia do bardzo specjalistycznych. Możesz nawet przekazać swoje własne BufferedImageOps, które mają być zastosowane do obrazu (a biblioteka automatycznie naprawi dla ciebie sześcioletni błąd BufferedImageOp JDK!).

Jest dużo więcej rzeczy do skalowania obrazów w Javie z powodzeniem, że biblioteka robi dla ciebie, na przykład zawsze utrzymując obraz w jednym z najlepiej obsługiwanych typów obrazu RGB lub ARGB podczas pracy na nim. Pod pokrywami potok przetwarzania obrazu Java2D cofa się do gorszego oprogramowania, jeśli typ obrazu wykorzystywany do jakichkolwiek operacji na obrazie jest słabo obsługiwany.

Jeśli wszystko to brzmi jak ból głowy, to jest coś takiego ... dlatego napisałem bibliotekę i otworzyłem ją, więc ludzie mogli zmienić rozmiar swoich zdjęć i przejść do ich życia bez konieczności martwienia się o to.

Nadzieję, że pomaga.

+10

Wystarczy spojrzeć na tę bibliotekę i bardzo pomogło; zrobiłby +2 ten wpis, gdybym mógł, z innym +1 dla faktu, że twój projekt ma swój własny hosting maven repo. Dzięki! –

+0

Dziękuję za miłe słowa Andrew, cieszę się, że znalazłeś to pomocne! –

+0

+1 dla @RiyadKalla za pisanie Scalr !!! – flybywire

3

Używam tych dwóch metod do skalowania obrazów, gdzie max to większy wymiar obrazu docelowego. Dla obrazu 100x100 to będzie 100, do 200x300 obraz będzie 300.

public static BufferedImage scale(InputStream is, int max) { 
    Image image = null; 
    try { 
     image = ImageIO.read(is); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    int width = image.getWidth(null); 
    int height = image.getHeight(null); 
    double dWidth = 0; 
    double dHeight = 0; 
    if (width == height) { 
     dWidth = max; 
     dHeight = max; 
    } 
    else if (width > height) { 
     dWidth = max; 
     dHeight = ((double) height/(double) width) * max; 
    } 
    else { 
     dHeight = max; 
     dWidth = ((double) width/(double) height) * max; 
    } 
    image = image.getScaledInstance((int) dWidth, (int) dHeight, Image.SCALE_SMOOTH); 
    BufferedImage bImage = toBufferedImage(image); 
    return bImage; 

} 

public static BufferedImage toBufferedImage(Image img) 
{ 
    if (img instanceof BufferedImage) 
    { 
     return (BufferedImage) img; 
    } 

    BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); 

    Graphics2D bGr = bimage.createGraphics(); 
    bGr.drawImage(img, 0, 0, null); 
    bGr.dispose(); 

    return bimage; 
} 
Powiązane problemy