[Java] Bilder skalieren

krizzelfix

Commander
Registriert
Sep. 2005
Beiträge
2.626
Hallo User,

habe mal wieder ein Problem mit Java und Bildern.
Diesmal will ich die größe eines Bildes verändern und als Thumbnail speichern.
Habe es erst über die Graphics Klasse probiert.
Code:
 try{
            Graphics g = pic.createGraphics();
            int height, width;
            
            if (pic.getWidth() > pic.getHeight()) {
                height = size;
                width = pic.getHeight() / pic.getWidth() * size;
            }
            else {
                width = size;
                height = pic.getWidth() / pic.getHeight() * size;
            }
            g.drawImage(pic, 0, 0, width, height, null);
            g.dispose();

            return pic;
        }
Das hat aber überhaupt nicht geklappt.
Dann habe ich es mit Image.getSacaledInstance probiert, mit genau so viel erfolg.
Code:
 try{
            int height, width;
            if (pic.getWidth() > pic.getHeight()) {
                height = size;
                width = pic.getHeight() / pic.getWidth() * size;
            }
            else {
                width = size;
                height = pic.getWidth() / pic.getHeight() * size;
            }
            Image thumb = pic.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
            return thumb;
        }
Und so rufe ich das ganze auf:
Code:
for(int i = 0; i < files.length; i++)
                            {
                                BufferedImage tmpImg = ImageIO.read(files[i]);
                                Picture Beschriften = new Picture(tmpImg);
                                Beschriften.CreatThumb(640);
                                Beschriften.WriteText(jTextField3.getText());
                                if (!Beschriften.SaveImage(targetDir+"/"+files[i].getName().toString()))
                                {
                                    errors++;
                                }
                                
                                Picture Thumb = new Picture(tmpImg);
                                Thumb.CreatThumb(160);
                                if (!Thumb.SaveImage(thumbDir+"/"+files[i].getName().toString()))
                                {
                                    errors++;
                                }
                            }
Kann mir jemand sagen was ich falsch mache?
Oder welchen Lösungsansatz ich verfolgen sollte?

Grüße

tewes
 
Ich würde mal sagen, daß der zweite Ansatz genau der richtige ist.

Dein Problem ist nur: Wenn du zwei ints dividierst, bekommst du immer einen int. Du brauchst aber einen float. Also musst du width und height als float deklarieren. Da getScaledInstance() aber als Höhe und Breite ints haben will, benutzt du die Methode round() aus der Klasse Math:
Code:
float height = 320;
float width = 200;
int size = 20;
float result = height / width * size;
pic.getScaledInstance(Math.round(size), Math.round(result), Image.SCALE_AREA_AVERAGING);
Außerdem:
Du hast bei der Berechnung der neuen Maße die Divisionen verwechselt:
Code:
if (pic.getWidth() > pic.getHeight()) {
  height = size;
                   width = pic.getHeight() / pic.getWidth() * size;
            }
            else {
                   width = size;
                   height = pic.getWidth() / pic.getHeight() * size;
            }
Mal angenommen, du hast ein Bild mit 320x200 Pixel Größe und willst es auf 32 Pixel runterrechnen (Ich nehme mal an, das deine size die minimale Ausdehnung, egal welche Richtung, darstellen soll). Dann bekommst du folgendes:

pic.getWidth() == 320;
pic.getHeight() == 200;

Damit wird die if-Abfrage true und du rechnest:

height = 32;
width = (200 / 320) * 32 = 20;

Vorher war aber die Breite größer als die Höhe
 
Zuletzt bearbeitet:
Hallo Cobinja,

hast du den Code irgendwo im einsatz?
Wenn ich bei mir die feststehenden Werte, mit dem vom Bild ersetzte, wird das Bild trotzdem in der Original Größe gespeichert.
Code:
            float height = pic.getHeight();
            float width = pic.getWidth();
            float result = height / width * size;
            pic.getScaledInstance(Math.round(size), Math.round(result), Image.SCALE_AREA_AVERAGING);
Grüße

tewes
 
Die Berechnung der neuen Bildgröße findet in der Methode CreatThumb() statt? Wenn ja, musst du beim Aufrufen der Methode auch dafür sorgen, daß das Ergebnis gespeichert wird. So wie du es im Moment aufrufst, schmeisst du das Ergebnis ins Nirvana und schreibst das Original in die Datei.
 
meine Methode sieht z.Z. so aus
Code:
 public Image CreatThumb(int size) {
        try{
            float height = pic.getHeight();
            float width = pic.getWidth();
            float result = height / width * size;
            pic.getScaledInstance(Math.round(size), Math.round(result), Image.SCALE_AREA_AVERAGING);
            return pic;
        }
        catch(Exception e){
            errors.add(e.getMessage());
        }
        return null;
    }
Da geht die Berechnung ja nicht verlohren.
 
Doch. Mach es besser so:
Code:
public Image CreatThumb(int size) {
        try{
            float height = pic.getHeight();
            float width = pic.getWidth();
            float result = height / width * size;
            return pic.getScaledInstance(Math.round(size), Math.round(result), Image.SCALE_AREA_AVERAGING);
        }
        catch(Exception e){
            errors.add(e.getMessage());
        }
        return null;
    }
Und dort wo die Methode aufgerufen wird, wirds auch weggeworfen:
Code:
for(int i = 0; i < files.length; i++)
{
  BufferedImage tmpImg = ImageIO.read(files[i]);
  Picture Beschriften = new Picture(tmpImg);

  // hier wirds weggeworfen
  Beschriften.CreatThumb(640);

  Beschriften.WriteText(jTextField3.getText());
  if (!Beschriften.SaveImage(targetDir+"/"+files[i].getName().toString()))
  {
    errors++;
  }
  Picture Thumb = new Picture(tmpImg);

  // und hier auch
  Thumb.CreatThumb(160);

  if (!Thumb.SaveImage(thumbDir+"/"+files[i].getName().toString()))
  {
    errors++;
  }
}
Speicher es jeweils in einem eigenen Picture- oder Image-Objekt und schreib es in die Datei.
 
Warum gehen denn die Daten verlohren?
Wenn ich Beschriften.WriteText() aufrufe und es speicher funktioniert es auch.
 
Da bearbeitest du aber auch das Original nur innerhalb der Klasse. Bei getScaledInstance() bleibt das Original unangetastet, diese Methode gibt das verkleinerte als Rückgabewert zurück. Also musst du es auch irgendwo hintun, um es zu speichern.
 
Also könnte ich in der Methode CreateThumb einfach pic = pic.getScaledImage() aufrufen?
Damit würde ja das verkleinerte Bild in der Klasse gespeichert werden.

//Edit: habs grad so probiert. Klappt aber auch nicht.
 
Zuletzt bearbeitet:
Kannst du eventuell mal den kompletten Code zippen und als Anhang hier reinstellen? Dann kann ich mir genau deinen Code mal angucken.
 
Ja mach ich sobald ich wieder zu Hause bin.

//Edit: ganz Projekt ist im Anhang.
 

Anhänge

Zuletzt bearbeitet:
Problem erkannt, Problem gebannt.

getScaledImage() liefert nicht ganz das zurück was gebraucht wurde, nämlich einfach nur ein Image, kein BufferedImage. Das muss man dann erst mal umformen.

Ich hab mal die Klassen Picture und IITSImage neu geschrieben, so dass sie "funktionieren". Du findest sie im Anhang. Es kann sein, dass du in NetBeans die Klasse IITSImage nur im Quelltext bearbeiten kannst, nicht visuell. Das liegt dann daran, dass ich Eclipse benutze. Bei mir wird keine IITSImage.form-Datei angelegt.

Guck einfach mal in beide Klassen rein und vergleich sie mit deinen. In IITSForm gibt es bei mir ganz am Ende die Methode saveImages(). In dieser wird die ganze Aktion durchgeführt. Meine Klasse Picture hat (mindestens) die gleichen Methoden wie deine. Alles relevante von mir ist in der Methode createThumb(). Ich hab die Methodennamen bei mir geringfügig anders genannt, sodass alle jetzt mit einem kleinen Buchstaben beginnen (ist quasi Java-Standard).
 

Anhänge

Vielen dank Cobinja,

das Programm funktioniert jetzt so wie ich es mir vorgestellt habe.

Haben aber noch Fragen.
Wir kann ich es machen das eingegeben Daten gespeichert werden und nach dem Neustart des Porgamms wieder anzeigen?
Warum wird bei img.getWidth(null) immer null übergeben?

Grüße

tewes
 
Das img.getWidth(null) kommt daher, daß die Methode eigentlich ein ImageObserver-Objekt als Parameter haben möchte. Es wird von der Klasse Component implementiert, um Bilder überwachen zu können (Ein Image wird üblicherweise in der GUI dargestellt). Da in der Klasse Picture aber ein solches Objekt nicht greifbar ist, wird einfach null übergeben. Es würde auch keinen Sinn machen, da das Objekt img nirgendwo dargestellt wird.

Zum speichern meinst du die beiden Pfade? Guck dir mal die Klasse Properties an. Damit kannst du Einstellungen in eine Datei speichern und auch wieder laden.
 
Zurück
Oben