Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Freistellen eines Bitmap klappt bei einigen Bildern nicht
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

Freistellen eines Bitmap klappt bei einigen Bildern nicht

beantworten | zitieren | melden

Guten Abend zusammen,
ich hänge jetzt schon seit einigen Tagen an der Freistellung eines Bitmaps fest.
Ich möchte anhand eines Rectangles ein Gesicht ausschneiden.
Dies mache ich mit Bitmap.Clone().
Bei einigen Bildern kappt es auch, bei einigen jedoch nicht. Der Ausschnitt ist viel zu klein.
Mir ist aufgefallen, dass es immer bei Bildern klappt, bei denen keine Informationen zur Auflösung gespeichert sind.

Gibt es einen Weg, die Auflösung zu berücksichtigen? Bitmap.SetResolution bringt nichts.
Hier mein Code:


public Image cropImage(Image img, Rectangle cropArea)
        {                           
            Bitmap bmpImage = new Bitmap(img);
            bmpImage.SetResolution(800, 800);                        
            return bmpImage.Clone(cropArea, bmpImage.PixelFormat);
        }
Viele Grüße,
private Nachricht | Beiträge des Benutzers
T-Virus
myCSharp.de - Member



Dabei seit:
Beiträge: 1.987
Herkunft: Nordhausen, Nörten-Hardenberg

beantworten | zitieren | melden

Auf Stack Overflow gibt es einen Lösungsansatz, der vermutlich helfen kann.
how-to-resize-an-image-c-sharp

T-Virus
Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

@T-Virus: Danke für deine Antwort, hat leider keine Abhilfe geschaffen.
private Nachricht | Beiträge des Benutzers
T-Virus
myCSharp.de - Member



Dabei seit:
Beiträge: 1.987
Herkunft: Nordhausen, Nörten-Hardenberg

beantworten | zitieren | melden

Sollte eigentlich nicht sein, da ich eine ähnliche Methode auch verwendet habe und diese resized meine Image Instanzen korrekt.
Wie sieht dein Code aus?
Bzw. wie verwendest du das Ergebnis?

T-Virus
Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

Mein Code sieht nun wie folgt aus:


 public Image cropImage(Image img, Rectangle cropArea)
        {           
            Bitmap bmpImage = new Bitmap(cropArea.Width, cropArea.Height);
            bmpImage.SetResolution(800, 800);

            using(var g = Graphics.FromImage(bmpImage))
            {
                g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

                using (var wrapMode = new ImageAttributes())
                {
                    wrapMode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
                    g.DrawImage(img, cropArea, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, wrapMode);
                }
            }
                        
#if DEBUG
            bmpImage.Save("C:\\Users\\User\\Pictures\\Scans\\test.jpg", ImageFormat.Jpeg);
#endif
            return bmpImage;
        }
Derzeit speichere ich das Bild nur erstmal in einem Verzeichnis ab.
Die Funktion liefert nun bei mir in keinem Fall ein brauchbares Ergebnis.

Folgendes wäre ggf. noch gut zu wissen:
Die Rectangles zum freistellen stammen bei mir wahlweise aus der Azure Gesichtserkennung oder werden freihand auf ein Canvas gezeichnet, welches das Image im Hintergrund hat.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.852

beantworten | zitieren | melden

Wenn Du mit der Azure API arbeitest, dann stimmt vielleicht die Logik nicht..?
Die Face API gibt Dir ja die erkannten Gesichter in ner Liste an, und jedes erkannte Gesicht liefert ein FaceRectangle.
Dort sind die Rectangles aber nicht als Koordinaten hinterlegt, sondern als Positionen (Height, Left, Right, Width).
Man erkennts nun am Code nicht, aber hast Du das beachtet, dass Du das beim Croppen erst umrechnen musst?

Wir haben damit eigentlich kein Problem gehabt die Gesichter raus zu schneiden oder ähnliche Effekte wie Du.
Wir arbeiten aber direkt mit GitHub - SixLabors/ImageSharp: A modern, cross-platform, 2D Graphics library for .NET und mit der Quelle als Bitmaps, ohne Image. Evtl. macht das Image noch irgendwas damit, was bei Dir blockt.


using (MemoryStream ms = new MemoryStream())
using (Image image = Image.Load(ms, out IImageFormat format))
{
    var clone = image.Clone(
          i => i.Resize(width, height).Crop(new Rectangle(x, y, cropWidth, cropHeight))
    );

    clone.Save(outStream, format);
}

Ich mag da bei Grafikoperationen nicht ganz bewandert zu sein, aber müsste man nicht erst das Bild laden und dann croppen?
Du hast erzeugst erst die Zielfläche und schreibst dann das Image rein....?
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2.105

beantworten | zitieren | melden

Vorweg ich hab zu Gesichtserkennung überhaupt keinen Einblick.

Mich wundert dass hier die Auflösung des Bilds eine Rolle spielen soll. Sollten das nicht alles absolute Pixelangaben sein? Da sollte es völlig irrelevant sein welche Auflösung man dem Bild andichtet, die es später vielleicht beim Ausdruck oder sonstiger Darstellung haben soll.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

@Abt:
Ich habe es jetzt auch mal mit Imagesharp versucht.
Ein Problem habe ich jetzt nur:
Wenn ich das Bild speichere, wird mir angezeigt, dass Dateiformat nicht unterstützt wird.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

Edit:
Mit der ImageSharp habe ich es nun ans Laufen bekommen.
Jedoch habe ich hier die gleichen Probleme wie zuvor.
Einige Bilder lassen sich korrekt croppen und einige croppen an falscher Stelle.
Die, bei denen es klappt haben eine Auflösung von 96.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.852

beantworten | zitieren | melden

Wenn Du die falschen Begriffe verwendest, dann wird die Lösungssuche für Dich und für uns schwer.
Ich hätte immer noch null Ahnung gehabt, was Du mit Auflösung meinst, wenn Du nicht die 96 als Zahl genannt hättest. Das ist nicht die Auflösung, sondern die Maßeinheit der Auflösung, auch Pixeldichte genannt.

Die DPI kannst Du mit SetResolution an der Bitmap setzen.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

Entschuldige, bin auf dem Gebiet auch relativ neu.
Das hatte ich bereits versucht. Macht keinen Unterschied.
Vllt noch ein paar mehr Details

Hier der aktuelle Quellcode:


using (var inStream = new MemoryStream(bildByte))
                using (var outStream = new MemoryStream())                    
                using (var image1 = SixLabors.ImageSharp.Image.Load(inStream, out IImageFormat format))
                {                         
                    
                    image1.Mutate(
                        i => i.Resize(image1.Width, image1.Height)
                              .Crop(new SixLabors.ImageSharp.Rectangle(x, y, width, height)));
                   
                    image1.Save("C:\\Users\\User\\Pictures\\Scans\\test.jpeg");            
                }

Ich habe auch nochmal die Bilder angehangen.
Attachments
private Nachricht | Beiträge des Benutzers
T-Virus
myCSharp.de - Member



Dabei seit:
Beiträge: 1.987
Herkunft: Nordhausen, Nörten-Hardenberg

beantworten | zitieren | melden

Zwei Anmerkungen.

1. Der outStream ist ungenutzt, kannst du weglassen.
2. Dein Resize bekommt die Größe/Breite vom Bild selbst, entsprechend ändert sich auch an dem nichts.
Vermutlich musst du hier deine eigene Höhe/Brite hingeben, so wie bei Crop schon geschehen?

T-Virus
Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

@T-Virus:
Wenn ich das mache, bekomme ich folgenden Fehler:
"Crop rectangle should be smaller than the source bounds."
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.852

beantworten | zitieren | melden

Kurze Frage: kopierst Du nur planlos Code und probierst wild nach try-and-error rum, oder willst Du auch verstehen, was Du da machst? ;-)

Deine Fehlermeldung sagt ja deutlich, dass das Quellbild kleiner als das Rectangle ist. Das kann ja nicht sein.
Dein gefundenes Rectangle kann niemals größer sein als das Bild. Les die Bitmap / ImageSharp Doku, verstehe das System. Debugge Dein Code und schau nach, wo der Fehler ist.
Gerade wenn man neu in dem Thema ist muss man halt viel lesen. Das ist der Job eines Entwicklers.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

Nene, ich probiere und lese mich da schon seit mehr als einer Woche durch.
Ich verstehe mehr oder weniger das System, ich verstehe nur nicht, warum es bei dem einen Bild klappt und beim anderen nicht.

Und dass das Quellbild nicht größer als das Rectangle sein kann ist klar, wenn man die Größe auf die des Rectangles setzt. Hatte nur das Feedback zu deinem T-Virus weitergeben wollen.
Weil ich schon vieles probiert habe, klammere ich mich mittlerweile an jeden Strohhalm.
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2.105

beantworten | zitieren | melden

Sind die Werte für die neue Bildgröße schlüssig? Vergleiche sie mit dem Bild was als Resultat herauskommt. Dann siehst du ob deine Crop Routine nicht passt, oder ob die neue Bildgröße schon nicht stimmt.
Wenn die Methoden zur Gesichtserkennung schon unsinnige Werte zurückgeben, kann natürlich kein sinniges Bild mehr herauskommen.

Und wie gesagt, dass das immer nur bei der Auflösung von 96 funktioniert ist wirklich komisch. Die Auflösung hat meiner Meinung hier überhaupt nichts zu suchen.
private Nachricht | Beiträge des Benutzers
johannes0405
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

So gestern Abend im Bett ist mir die entscheidende Lösung eingefallen.
Die Standard Windows Auflösung ist ja 96 DPI. Deswegen funktioniert es auch mit Bildern die eine Auflösung von 96 DPI besitzen.
Die Bilder mit einer abweichenden Auflösung muss ich also auf die DPI runterrechnen.
D. h. x, y, width und height des Rectangles für den Bildausschnitt müssen angepasst werden auf die Auflösung des Bildes.
Mache ich jetzt wie folgt:
Beispiel anhand der Width:


var widthneu = (int)((rectangle.Width / 96.0) * image1.Metadata.HorizontalResolution);
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von johannes0405 am .
private Nachricht | Beiträge des Benutzers