{"id":54,"date":"2015-04-23T15:30:48","date_gmt":"2015-04-23T13:30:48","guid":{"rendered":"http:\/\/blog.dtp-soft.de\/?p=54"},"modified":"2015-05-13T13:30:21","modified_gmt":"2015-05-13T11:30:21","slug":"c-schneller-inhaltlicher-bildvergleich","status":"publish","type":"post","link":"http:\/\/dtp-soft.de\/?p=54","title":{"rendered":"c#: Schneller inhaltlicher Bildvergleich"},"content":{"rendered":"<p><strong>Bilder k\u00f6nnen unter .NET nicht direkt per Equals verglichen werden, weil die Methode von <a title=\"MSDN: System.Object\" href=\"https:\/\/msdn.microsoft.com\/de-de\/library\/system.object(v=vs.110).aspx\">System.Object<\/a> geerbt ist und nicht explizit \u00fcberschrieben wird. Daher wird bei einem <span class=\"lang:c# decode:true  crayon-inline \">image1.Equals(image2)<\/span>\u00a0\u00a0nur ein einfaches <a title=\"MSDN: Object.ReferenceEquals\" href=\"https:\/\/msdn.microsoft.com\/de-de\/library\/system.object.referenceequals(v=vs.110).aspx\">Object.ReferenceEquals<\/a> durchgef\u00fchrt.<\/strong><!--more--><\/p>\n<p>Tats\u00e4chlich muss man die Eigenschaften der Bilddaten miteinander vergleichen. In Form einer Erweiterungsmethode kann das zum Beispiel so aussehen:<\/p>\n<pre class=\"lang:c# decode:true \" title=\"korrekter Bildvergleich des Inhalts\">public static bool ContentEquals(this Image img1, Image img2)\r\n{\r\n    \/\/zuerst ein ReferenceEquals durchf\u00fchren, wenn das schon stimmt, kann man sich den Rest sparen\r\n    if (object.Equals(img1, img2)) return true;\r\n    \/\/dann pr\u00fcfen, ob ein Bild null ist (w\u00e4ren es beide, w\u00e4re voriger Vergleich schon true gewesen)\r\n    if (left == null || right == null) return false;\r\n    if (!left.Size.Equals(right.Size) || !left.PixelFormat.Equals(right.PixelFormat)) return false;\r\n\r\n    Bitmap img1bmp = img1 as Bitmap;\r\n    Bitmap img2bmp = img2 as Bitmap;\r\n    if (img1bmp == null || img2bmp == null) return true;\r\n\r\n    int bytes = img1.Width * img1.Height * (Image.GetPixelFormatSize(img1.PixelFormat) \/ 8);\r\n\r\n    bool result = true;\r\n    byte[] b1 = new byte[bytes];\r\n    byte[] b2 = new byte[bytes];\r\n\r\n    BitmapData data1 = null;\r\n    BitmapData data2 = null;\r\n    try\r\n    {\r\n        data1 = img1bmp.LockBits(new Rectangle(0, 0, img1bmp.Width - 1, img1bmp.Height - 1), ImageLockMode.ReadOnly, img1bmp.PixelFormat);\r\n        data2 = img2bmp.LockBits(new Rectangle(0, 0, img2bmp.Width - 1, img2bmp.Height - 1), ImageLockMode.ReadOnly, img2bmp.PixelFormat);\r\n\r\n        Marshal.Copy(data1.Scan0, b1, 0, bytes);\r\n        Marshal.Copy(data2.Scan0, b2, 0, bytes);\r\n\r\n        result = b1.SequenceEqual(b2);\r\n    }\r\n    finally\r\n    {\r\n        if (bmd1 != null) img1bmp.UnlockBits(data1);\r\n        if (bmd2 != null) img2bmp.UnlockBits(data2);\r\n    }\r\n    return result;\r\n}<\/pre>\n<p>Wichtig ist auch, dass die mit <a title=\"MSDN: LockBits\" href=\"https:\/\/msdn.microsoft.com\/de-de\/library\/5ey6h79d(v=vs.110).aspx\">LockBits<\/a> gesperrten Daten auf jeden Fall wieder freigegeben werden, daher ist es in einen try-Block mit finally eingeschlossen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bilder k\u00f6nnen unter .NET nicht direkt per Equals verglichen werden, weil die Methode von System.Object geerbt ist und nicht explizit \u00fcberschrieben wird. Daher wird bei einem image1.Equals(image2)\u00a0\u00a0nur ein einfaches Object.ReferenceEquals durchgef\u00fchrt.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[5],"tags":[20,6,21,19],"class_list":["post-54","post","type-post","status-publish","format-standard","hentry","category-c","tag-bitmap","tag-c","tag-equals","tag-image"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/dtp-soft.de\/index.php?rest_route=\/wp\/v2\/posts\/54","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/dtp-soft.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/dtp-soft.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/dtp-soft.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/dtp-soft.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=54"}],"version-history":[{"count":6,"href":"http:\/\/dtp-soft.de\/index.php?rest_route=\/wp\/v2\/posts\/54\/revisions"}],"predecessor-version":[{"id":66,"href":"http:\/\/dtp-soft.de\/index.php?rest_route=\/wp\/v2\/posts\/54\/revisions\/66"}],"wp:attachment":[{"href":"http:\/\/dtp-soft.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=54"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/dtp-soft.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=54"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/dtp-soft.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=54"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}