Jak mogę zmienić rozmiar obrazu za pomocą Java?

126

Muszę zmienić rozmiar plików PNG, JPEG i GIF. Jak mogę to zrobić za pomocą Java?

pbreault
źródło
14
ponieważ poprosiłeś o bibliotekę, prawdziwa odpowiedź jest tutaj: stackoverflow.com/questions/244164/… . Jest dużo łatwiejszy w użyciu niż kod w zaakceptowanej odpowiedzi;)
Artur Gajowy
Nie zgadzam się z częścią biblioteczną: Graphics2D jest częścią biblioteki awt i jest open source. Jeśli chodzi o ostatnią część (open source), nie jestem w 100% pewien, ale kto i tak spojrzałby na kod AWT?
Burkhard

Odpowiedzi:

87

Po załadowaniu obrazu możesz spróbować:

BufferedImage createResizedCopy(Image originalImage, 
            int scaledWidth, int scaledHeight, 
            boolean preserveAlpha)
    {
        System.out.println("resizing...");
        int imageType = preserveAlpha ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, imageType);
        Graphics2D g = scaledBI.createGraphics();
        if (preserveAlpha) {
            g.setComposite(AlphaComposite.Src);
        }
        g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null); 
        g.dispose();
        return scaledBI;
    }
Burkhard
źródło
9
Odkryłem, że ta technika tworzy obraz, który nie jest wystarczająco wysokiej jakości dla moich potrzeb.
morgancodes
1
czy Image.getScaledInstance (szerokość, wysokość, podpowiedzi) również zrobi?
gra kodowa
1
czy preserveAlpha sprawdza nieprawidłowo (dla imageType)?
Thilo
Zgadzam się z @morgancodes. Jakość obrazu jest znacznie gorsza niż to, co otrzymujesz, na przykład OS X Preview podczas zmiany rozmiaru do tych samych wymiarów. Spróbuje niektórych bibliotek open-source, aby sprawdzić, czy radzą sobie lepiej.
Thilo
1
„Spróbuje niektórych bibliotek open-source, aby sprawdzić, czy wypadną lepiej”. +1 dla imgscalr z odpowiedzi @ RiyadKalla.
Thilo
182

FWIW Właśnie wydałem (Apache 2, hostowany na GitHub) prostą bibliotekę skalowania obrazu dla Javy o nazwie imgscalr (dostępna w centrali Maven ).

Biblioteka wdraża kilka różnych podejść do skalowania obrazu (w tym podejście przyrostowe Chrisa Campbella z kilkoma drobnymi ulepszeniami) i albo wybierze najbardziej optymalne podejście, jeśli o to poprosisz, albo da ci najszybsze lub najlepiej wyglądające (jeśli poproś o to).

Użycie jest banalnie proste, tylko kilka statycznych metod. Najprostszy przypadek użycia to:

BufferedImage scaledImage = Scalr.resize(myImage, 200);

Wszystkie operacje zachowują oryginalne proporcje obrazu, więc w tym przypadku prosisz imgscalr o zmianę rozmiaru obrazu w granicach 200 pikseli szerokości i 200 pikseli wysokości i domyślnie automatycznie wybierze najlepiej wyglądające i najszybsze podejście do tego, ponieważ nie było nie określono.

Na początku zdaję sobie sprawę, że wygląda to na autopromocję (tak jest), ale spędziłem sporo czasu na szukaniu w Google dokładnie tego samego tematu i wymyślałem różne wyniki / podejścia / przemyślenia / sugestie i postanowiłem usiąść i napisać prosta implementacja, która rozwiązałaby te 80-85% przypadków użycia, w których masz obraz i prawdopodobnie chcesz dla niego miniatura - tak szybko, jak to możliwe, albo tak dobrze wyglądająca, jak to tylko możliwe (dla tych, którzy próbowali, zauważysz wykonanie Graphics.drawImage nawet z interpolacją BICUBIC do wystarczająco małego obrazu, nadal wygląda jak śmieci).

Riyad Kalla
źródło
1
Riyad, lubiłem używać Scalra. Ciekaw jestem, jak ostatecznie wybrałeś API ze wszystkimi statycznymi metodami? Napisałem podobny interfejs API, który był bliższy konstruktorowi. Lubię new ImageScaler(img).resizeTo(...).rotate(...).cropTo(...).toOutputBuffer(). Ja też lubię twój sposób i myślę, że jest prostszy.
Amir Raminfar
4
Amir, wzorzec konstruktora działa również wspaniale dla tego typu bibliotek, akurat akurat zdecydowałem się na podejście metody statycznej, ponieważ pomyślałem, że jest to włosy łatwiejsze do naśladowania dla nowych użytkowników i wzorca użycia, którego oczekiwałem od imgscalr (pierwotnie tylko pojedyncze metody zmiany rozmiaru) nie korzystały z posiadania stanu przechowywania instancji (instancja programu budującego). Więc zaoszczędziłem na instancji obiektu i poszedłem z metodami statycznymi. Przyjąłem zdecydowane stanowisko przeciwko tworzeniu obiektów wewnątrz imgscalr w każdym miejscu, w którym mogłem tego uniknąć. Oba podejścia działają jednak świetnie.
Riyad Kalla
5
+1 za udostępnienie tego w centralnym repozytorium Mavena!
Grilse,
Co to jest klasa BufferedImage, dlaczego studio android jej nie znajduje? @Riyad Kalla
Milad
3
@ xe4me BufferedImage to klasa w J2SE JDK (część środowiska Java2D) używana do reprezentowania surowych, nieskompresowanych danych pikseli. Nie jest dostępny na Androida, Android udostępnia własne klasy do pracy z danymi obrazu.
Riyad Kalla
54

Thumbnailator to otwarta biblioteka do zmiany rozmiaru obrazu dla języka Java z płynnym interfejsem, rozpowszechniana na licencji MIT.

Napisałem tę bibliotekę, ponieważ tworzenie wysokiej jakości miniatur w Javie może być zaskakująco trudne, a wynikowy kod może być dość niechlujny. Dzięki Thumbnailator możliwe jest wyrażanie dość skomplikowanych zadań za pomocą prostego, płynnego interfejsu API.

Prosty przykład

Dla prostego przykładu, zrobienie obrazu i zmiana jego rozmiaru do 100 x 100 (z zachowaniem proporcji oryginalnego obrazu) i zapisanie go do pliku można osiągnąć w jednej instrukcji:

Thumbnails.of("path/to/image")
    .size(100, 100)
    .toFile("path/to/thumbnail");

Zaawansowany przykład

Wykonywanie złożonych zadań zmiany rozmiaru jest uproszczone dzięki płynnemu interfejsowi Thumbnailator.

Załóżmy, że chcemy wykonać następujące czynności:

  1. weź zdjęcia w katalogu i
  2. zmień ich rozmiar na 100 x 100, zachowując proporcje oryginalnego obrazu,
  3. zapisz je wszystkie w plikach JPEG z ustawieniami jakości 0.85 ,
  4. gdzie nazwy plików są pobierane z oryginału z thumbnail.dołączeniem na początku

Po przetłumaczeniu na Thumbnailator moglibyśmy wykonać powyższe z następującymi rozwiązaniami:

Thumbnails.of(new File("path/to/directory").listFiles())
    .size(100, 100)
    .outputFormat("JPEG")
    .outputQuality(0.85)
    .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

Uwaga dotycząca jakości i szybkości obrazu

Ta biblioteka korzysta również z progresywnej metody skalowania bilinearnego , wyróżnionej w Filthy Rich Clients przez Chet Haase i Romain Guy, w celu generowania wysokiej jakości miniatur przy jednoczesnym zapewnieniu akceptowalnej wydajności w czasie wykonywania.

coobird
źródło
1
coobird, naprawdę niezła robota z API. Bardzo proste i łatwe w użyciu.
Riyad Kalla,
@LordT: Cieszę się, że Thumbnailator jest łatwy w użyciu :)
coobird
Jak mogę utworzyć miniaturę 80x80 z oryginalnym obrazem 500x400? nie widziałem żadnej opcji. dzięki!
frotte,
„.outputFormat („ JPEG ”)” nie jest nigdzie zapisane w dokumentacji.
Vincent Cantin
@Vincent Pełna lista metod, których można użyć, znajduje się w dokumentacji Thumbnailator API - thumbnailator.googlecode.com/hg/javadoc/net/coobird/…
coobird
12

Nie potrzebujesz do tego biblioteki. Możesz to zrobić z samą Javą.

Chris Campbell ma doskonały i szczegółowy opis skalowania obrazów - zobacz ten artykuł .

Chet Haase i Romain Guy również szczegółowo opisali skalowanie obrazu w swojej książce Filthy Rich Clients .

David Koelle
źródło
7
To właśnie artykuł Chrisa zmotywował mnie do napisania imgscalr; i takie pytania (i odpowiedzi podobne do Twoich). Wiele osób w kółko pyta, jak uzyskać dobrze wyglądające miniatury z obrazu. Jest wiele sposobów, aby to zrobić, Chris nie zawsze jest najlepszy, zależy to od tego, co próbujesz zrobić i jak duża jest redukcja. imgscalr zajmuje się tym wszystkim i jest to 1 klasa.
Riyad Kalla
2
+1, zgadzam się, klienci Filthy Rich to jedna z najlepszych książek java dostępnych na równi z "Effective Java", ale ImgScalr jest tym, czego używam, ponieważ jestem leniwy.
Shawn Vader
9

Jeśli masz do czynienia z dużymi obrazami lub chcesz uzyskać ładny wygląd, nie jest to proste zadanie w Javie. Po prostu zrobienie tego poprzez zmianę skali za pomocą Graphics2D nie utworzy wysokiej jakości miniatury. Możesz to zrobić za pomocą JAI, ale wymaga to więcej pracy, niż byś sobie wyobrażał, aby uzyskać coś, co wygląda dobrze, a JAI ma paskudny zwyczaj wysadzania naszej JVM błędów OutOfMemory.

Sugeruję użycie ImageMagick jako zewnętrznego pliku wykonywalnego, jeśli możesz sobie z tym poradzić. Jest prosty w użyciu i dobrze spełnia swoje zadanie, więc nie musisz tego robić.

Joel Carranza
źródło
4

Jeśli posiadanie imagemagick zainstalowanego na maszynie jest opcją, polecam im4java . Jest to bardzo cienka warstwa abstrakcji w interfejsie wiersza poleceń, ale bardzo dobrze spełnia swoje zadanie.

naltatis
źródło
3

Interfejs API języka Java nie zapewnia standardowej funkcji skalowania obrazów i obniżania jakości obrazu.

Z tego powodu próbowałem użyć cvResize z JavaCV, ale wydaje się, że powoduje to problemy.

Znalazłem dobrą bibliotekę do skalowania obrazów: po prostu dodaj zależność dla "java-image-scaling" w swoim pom.xml.

<dependency>
    <groupId>com.mortennobel</groupId>
    <artifactId>java-image-scaling</artifactId>
    <version>0.8.6</version>
</dependency>

W repozytorium Maven otrzymasz najnowszą wersję tego.

Dawny. W twoim programie java

ResampleOp resamOp = new ResampleOp(50, 40);
BufferedImage modifiedImage = resamOp.filter(originalBufferedImage, null);
Ruju
źródło
Jest również dostępny na GitHub ( github.com/mortennobel/java-image-scaling.git ) i na razie ma wersję 8.7. Wymagało to naprawy w pom.xml (zmiana źródła i celu na co najmniej 1,6, ponieważ nowszy Maven nie obsługuje już wersji 1.5).
Cromax
2

Możesz spróbować użyć GraphicsMagick Image Processing System z im4java jako interfejsem wiersza poleceń dla Javy.

Jest wiele zalet GraphicsMagick, ale jedna dla wszystkich:

  • GM jest używany do przetwarzania miliardów plików w największych witrynach fotograficznych na świecie (np. Flickr i Etsy).
kajo
źródło
1

Wspomniano o Magii Obrazu. Istnieje projekt interfejsu JNI o ​​nazwie JMagick. Nie jest to szczególnie stabilny projekt (a sam Image Magick był znany z tego, że bardzo zmieniał, a nawet łamał kompatybilność). To powiedziawszy, mamy dobre doświadczenia w używaniu JMagicka i kompatybilnej wersji Image Magick w środowisku produkcyjnym do wykonywania skalowania z dużą przepustowością i małym opóźnieniem. Szybkość była znacznie lepsza niż w przypadku całej biblioteki graficznej Java, którą wcześniej próbowaliśmy.

http://www.jmagick.org/index.html

Jice
źródło
1

Po prostu użyj odpowiedzi Burkharda, ale dodaj tę linię po utworzeniu grafiki:

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

Możesz również ustawić wartość na BICUBIC, da to obraz o lepszej jakości, ale jest to operacja droższa. Istnieją inne wskazówki dotyczące renderowania, które możesz ustawić, ale odkryłem, że interpolacja daje najbardziej zauważalny efekt. Pamiętaj, że jeśli chcesz dużo powiększyć, kod java najprawdopodobniej będzie bardzo wolny. Zauważyłem, że większe obrazy zaczynają generować opóźnienie około 300% zoomu, nawet przy wszystkich wskazówkach dotyczących renderowania ustawionych na optymalizację pod kątem szybkości nad jakością.

Jedyny prawdziwy Colter
źródło
1

Okazuje się, że napisanie wydajnego skalera nie jest trywialne. Zrobiłem to raz dla projektu open source: ImageScaler .

Zasadniczo „java.awt.Image # getScaledInstance (int, int, int)” również by się nadawało, ale jest w tym paskudny błąd - szczegółowe informacje można znaleźć w moim linku.

aanno
źródło
0

Opracowałem rozwiązanie z swobodnie dostępnymi klasami (AnimatedGifEncoder, GifDecoder i LWZEncoder) dostępnymi do obsługi Animacji GIF.
Możesz pobrać jar jgifcode i uruchomić klasę GifImageUtil. Link: http://www.jgifcode.com

Rohit Tiwari
źródło
1
Podaj w odpowiedzi, czy jesteś powiązany z polecanym przez Ciebie produktem.
Hans Olsson
waw ... czy to służy do zmiany rozmiaru animowanego obrazu GIF? i to nic nie kosztuje? Wwaww ... powinienem spróbować teraz ...
gumuruh,
Tak, to zostało opracowane przeze mnie. Oto jego kod źródłowy github.com/rt29/jgifcode - to jest teraz tutaj. Już go nie popieram.
Rohit Tiwari
0

Jeśli nie chcesz importować imgScalr, takiej jak @Riyad Kalla odpowiedź, którą powyżej przetestowałem, działa dobrze, możesz to zrobić zaczerpniętą z odpowiedzi Petera Walsera @Peter Walser w innej kwestii:

 /**
     * utility method to get an icon from the resources of this class
     * @param name the name of the icon
     * @return the icon, or null if the icon wasn't found.
     */
    public Icon getIcon(String name) {
        Icon icon = null;
        URL url = null;
        ImageIcon imgicon = null;
        BufferedImage scaledImage = null;
        try {
            url = getClass().getResource(name);

            icon = new ImageIcon(url);
            if (icon == null) {
                System.out.println("Couldn't find " + url);
            }

            BufferedImage bi = new BufferedImage(
                    icon.getIconWidth(),
                    icon.getIconHeight(),
                    BufferedImage.TYPE_INT_RGB);
            Graphics g = bi.createGraphics();
            // paint the Icon to the BufferedImage.
            icon.paintIcon(null, g, 0,0);
            g.dispose();

            bi = resizeImage(bi,30,30);
            scaledImage = bi;// or replace with this line Scalr.resize(bi, 30,30);
            imgicon = new ImageIcon(scaledImage);

        } catch (Exception e) {
            System.out.println("Couldn't find " + getClass().getName() + "/" + name);
            e.printStackTrace();
        }
        return imgicon;
    }

 public static BufferedImage resizeImage (BufferedImage image, int areaWidth, int areaHeight) {
        float scaleX = (float) areaWidth / image.getWidth();
        float scaleY = (float) areaHeight / image.getHeight();
        float scale = Math.min(scaleX, scaleY);
        int w = Math.round(image.getWidth() * scale);
        int h = Math.round(image.getHeight() * scale);

        int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

        boolean scaleDown = scale < 1;

        if (scaleDown) {
            // multi-pass bilinear div 2
            int currentW = image.getWidth();
            int currentH = image.getHeight();
            BufferedImage resized = image;
            while (currentW > w || currentH > h) {
                currentW = Math.max(w, currentW / 2);
                currentH = Math.max(h, currentH / 2);

                BufferedImage temp = new BufferedImage(currentW, currentH, type);
                Graphics2D g2 = temp.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2.drawImage(resized, 0, 0, currentW, currentH, null);
                g2.dispose();
                resized = temp;
            }
            return resized;
        } else {
            Object hint = scale > 2 ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_BILINEAR;

            BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = resized.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(image, 0, 0, w, h, null);
            g2.dispose();
            return resized;
        }
    }
shareef
źródło
0

Wypróbuj następującą metodę:

ImageIcon icon = new ImageIcon("image.png");
Image img = icon.getImage();
Image newImg = img.getScaledInstance(350, 350, java.evt.Image.SCALE_SMOOTH);
icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, "image on The frame", "Display Image", JOptionPane.INFORMATION_MESSAGE, icon);
Fridjato Part Fridjat
źródło