Michi: zuschneiden eines Bildes auf 16zu9, vorher testen ob möglich

Hallo, bis jetzt bin ich ein eifriger Leser hier. Ich arbeite an einer php Funktion zum abspeichern von Bildern. diese sollen alle automatisch abgeschnitten werden, so das es anschließend alle Bilder die gleiche Grösse haben 1600x900.

Ich arbeite mit Bildern die alle grösser sind als 1600x900!

$PicturesInfo = getimagesize($altes_bild);
$OriginalBreite = $PicturesInfo[0];
$OriginalHoehe 	= $PicturesInfo[1];	
$OriginalGrafik 	= ImageCreateFromJPEG($altes_bild); // Original einlesen
$NeueGrafik 	= ImageCreateTrueColor(1600,900); // Neues Bild leer erstellen
imagecopyresampled($NeueGrafik, $OriginalGrafik, 0,0,0,0,1600,900,$OriginalBreite,$OriginalHoehe); // Ausschnitt rüberkopieren
ImageJPEG($NeueGrafik,'neues-bild.jpg'); // Bild speichern



Jetzt habe ich ein Problem, ich bekomme zwar das fertig Bild hin, aber es ist verzehrt.

Obwohl ich denke ich habe alles richtig gemacht (was ja anscheinend nicht sein kann)

Bilder mit dem Ergebniss habe ich angehängt, (sind frei zur Veröffentlichung)

Original Bild

Neues Bild, Proportionen stimmen nicht!

  1. Hallo, bis jetzt bin ich ein eifriger Leser hier. Ich arbeite an einer php Funktion zum abspeichern von Bildern. diese sollen alle automatisch abgeschnitten werden, so das es anschließend alle Bilder die gleiche Grösse haben 1600x900.

    Das scheint mir keine gute Idee. Wie Du ja an Deinem Beispiel schon gemerkt hast: wenn das Ausgangsbild nicht im Format 16:9 vorliegt, dann musst Du es beschneiden (keine Ahnung, wie das nativ mit PHP geht, z.B. ImageMagick kann das), um an unschöne Verzerrungen zu vermeiden. Entweder vertikal oder horizontal. Das hat dann aber auch seinen Preis: Du verlierst Bildinformation. Womöglich ganz Entscheidende.

    Du wirst die Nummer ja vermutlich letztlich machen wollen, weil Du eine gleichförmige Darstellung im Browser wünschst. Also löse das ganze doch einfach mit CSS und gib dem User die Möglichkeit, sich das Bild im Ausgangsformat anzeigen zu lassen. Dann hat man wenigstens nochdie Chance, evtl. abgeschnittene Bereiche doch noch zu sehen.

    Ich arbeite mit Bildern die alle grösser sind als 1600x900!

    Das ist gut. Physikalisches Upscaling sollte nur in wenigen Ausnahmefällen sinnvoll sein.

  2. Hallo,

    das was du im Betreff stehen hast, spiegelt sich aber so gar nicht in deiner Beschreibung und Code wider…

    Du sprichst von „zuschneiden“, verwendest dann aber „resize“ bzw. imagecopyresampled(). Hier wäre dann „crop“ das Mittel der Wahl.

    Und von vorher testen ist auch nichts zu sehen. Du musst die Rückgaben der verwendeten Funktionen auslesen, angefangen beim getimagesize(), das dir z.B. sagen kann, ob die Datei überhaupt ein Bild ist.

    Gruß
    Kalk

    1. das was du im Betreff stehen hast, spiegelt sich aber so gar nicht in deiner Beschreibung und Code wider…

      Ja, da hast du mehr als Recht.

      					
      $PicturesInfo = getimagesize($altes_bild);
      if(	$PicturesInfo[2]==2){				
      $im = ImageCreateFromJPEG($altes_bild); // Original einlesen
      $size = min(imagesx($im), imagesy($im));
      $im2 = imagecrop($im, ['x' => ($PicturesInfo[0]-1600)/2, 'y' => ($PicturesInfo[1]-900)/2, 'width' => 1600, 'height' => 900]);
      	if ($im2 !== FALSE) {
      		ImageJPEG($im2, $bild.'-1.jpg');
      		imagedestroy($im2);
      	}
      imagedestroy($im);
      }	
      

      Es funktioniert... Jetzt komme ich zum Titel Thema zurück!

      Um abzuklären ob das vorhandene Bild auch in meine Vorgaben passt.

      Michi

      1. Jetzt komme ich zum Titel Thema zurück!

        Um abzuklären ob das vorhandene Bild auch in meine Vorgaben passt.

        Du willst prüfen, ob sich das jeweilige Bild ohne Verzerrung / Beschnitt auf 1600x900 skalieren lässt? Andernfalls passiert nix? Klingt irgendwie komisch, aber die Lösung für die Aufgabe ist nicht so kompliziert. Teil mal die Breite des Bildes durch die Höhe und vergleich das Ergebnis dann mit 1600/900 ;-)

        1. Hallo,

          […] Klingt irgendwie komisch, aber die Lösung für die Aufgabe […]

          Und damit hätten wir die Antwort auf die Frage, was das eigentlich Schwierige am Programmieren ist. Nämlich erstmal die Aufgabe präzise zu formulieren…

          Gruß
          Kalk

          1. @@Tabellenkalk

            Und damit hätten wir die Antwort auf die Frage, was das eigentlich Schwierige am Programmieren ist. Nämlich erstmal die Aufgabe präzise zu formulieren…

            “Whoever best describes the problem is the person most likely to solve the problem.” —Dan Roam

            (gefunden in ChrisBs Signatur, irgendwann)

            😷 LLAP

            --
            “When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy.’ They told me I didn’t understand the assignment, and I told them they didn’t understand life.” —John Lennon
      2. Jetzt habe ich das Problem Nummer 2.

        Solange die Bilder nur etwas grösser waren ist mir das nicht aufgefallen, aber wenn sie deutlich grösser sind, habe ich ein Problem.

        Das neue Format ist ja 1600 zu 900.

        Aber wenn das Bild zum Beispiel 3200 zu 900, schneidet es genau 1600 zu 900 raus, in der Mitte.

        wie schaffe ich es das es verkleinert und rausschneidet.

        $PicturesInfo = getimagesize($altes_bild);
        if(	$PicturesInfo[2]==2) {				
        	$im = ImageCreateFromJPEG($altes_bild); // Original einlesen
        	$size = min(imagesx($im), imagesy($im));
        	$im2 = imagecrop($im, [
        		'x' => ($PicturesInfo[0]-1600)/2,
        		'y' => ($PicturesInfo[1]-900)/2,
        		'width' => 1600,
        		'height' => 900]);
        	if ($im2 !== FALSE) {
         		ImageJPEG($im2, $bild.'-1.jpg');
         		imagedestroy($im2);
        	}
        	imagedestroy($im);
        }	
        

        Edit Rolf B: Code als PHP markiert und ein paar Zeilenumbrüche für Lesbarkeit im Forum eingefügt

        1. Hallo michi,

          wenn das Bild 3200x900 ist, was willst Du dann verkleinern? Wenn dein Zielformat 1600x900 ist, müsstest Du bei Verkleinerung schwarze Balken ansetzen. Ein Ausgangsformat von 3200x1400 wäre vermutlich ein besseres Beispiel.

          Wenn Du skalieren + ausschneiden willst, dann musst Du erstmal berechnen, wie groß das größte Rechteck im Seitenverhältnis 16/9 ist, das in dein Ausgangsbild hineinpasst, dieses Rechteck in der Quelle zentrieren und das als Ausgangsposition für imagecopyresize (oder imagecopyresample) verwenden.

          Ich würde dafür erstmal das Seitenverhältnis $sourceAspect der Quelle bestimmen, und die Infos zum $targetAspect für die Lesbarkeit auch in eine Variable legen. Wenn Du den Kram in eine Funktion auslagerst, kannst Du $targetWidth und $targetHeight als Parameter übergeben. Reusability!

          $targetWidth = 1600;
          $targetHeight = 900;
          $targetAspect = $targetWidth / $targetHeight;
          $sourceAspect = $PictureInfo[0] / $PictureInfo[1];
          

          Wenn $sourceAspect kleiner ist als 16/9, ist das Bild "zu hoch", d.h. Du musst oben/unten was abschneiden. Ist es größer, ist es "zu breit" und Du musst oben seitlich [EDIT Der Martin] abschneiden. Den Fall "ist gleich" kannst Du einem dieser beiden Zweige zuordnen. D.h. je nach $sourceAspect übernimmst Du eine Bildkante und berechnest die Länge der anderen Kante an Hand des Ziel-Seitenverhältnisses.

          if ($sourceAspect <= $targetAspect) {
             // Bild ist "zu schmal" (bzw. "zu hoch")
             // Ausschnittsgröße an der Breite der Quelle orientieren
             $clipWidth = $PictureInfo[0];       
             $clipHeight = $PictureInfo[0] / $targetAspect;
          }
          else
          {
             // Bild ist "zu breit" (bzw. "zu niedrig")
             // Ausschnittsgröße an der Höhe der Quelle orientieren
             $clipWidth = $PictureInfo[1] * $targetAspect;
             $clipHeight = $PictureInfo[1];
          }
          
          $clipX = ($PictureInfo[0] - $clipWidth) / 2;
          $clipY = ($PictureInfo[0] - $clipHeight) / 2;
          

          Und damit hast Du die vier Input-parameter für imagecopyresize, die Du für die Lage der Quelle brauchst. Diese Logik dürfte übrigens auch für Bilder funktionieren, die kleiner als 1600x900 sind und hochskaliert werden müssen.

          Das ist ungetestet - keine Garantie 😉

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Geht, nur der Aschnitt ist noch nicht aktiv

            $neues_bild = imagecreatetruecolor($targetWidth,$targetHeight);
            $altes_bild = imagecreatefromjpeg($altes_bild);
            imagecopyresized($neues_bild,$altes_bild,0,0,0,0,  $targetWidth, $targetHeight, $PictureInfo[0], $PictureInfo[1]);
            

            So sollte der Abschnitt sein, doch das Ergebnis ist leider schwarz, ich denke bei der Zuordnung stimmt etwas nicht.

            $neues_bild = imagecreatetruecolor($targetWidth,$targetHeight);
            $altes_bild = imagecreatefromjpeg($altes_bild);
            imagecopyresized($neues_bild,$altes_bild,$clipX, $clipY ,$clipWidth , $clipHeight,  $targetWidth, $targetHeight, $PictureInfo[0], $PictureInfo[1]);
            
            1. Hallo Michi,

              in der Tat.

              Da steht:

              imagecopyresized (
                  resource $dst_image , resource $src_image ,
                  int $dst_x , int $dst_y , int $src_x , int $src_y ,
                  int $dst_w , int $dst_h , int $src_w , int $src_h ) : bool
              

              Vergleiche mit deinen Parametern.

              $PictureInfo[0] und $PictureInfo[1] brauchst Du nicht mehr, dafür hast Du clipWidth und clipHeight berechnet. Und irgendwo muss noch ein 0, 0 stehen, du möchtest ja dein Zielbild vollständig ausfüllen.

              Rolf

              --
              sumpsi - posui - obstruxi
              1. Danke

                mein Fehler war das ich total auf die beiden Werteint $dst_x , int $dst_y ,fixiert war und den Wald vor lauter Bäumen nicht gesehen habe.

                mir war nicht klar das das neue Bild ja von Haus aus 0,0 ist, weil es ja neu erstellt wurde, so habe ich immer versucht es einzupassen grrrrrrrrrrrr .

                Danke für die Hilfe