ब्लैकहाट कहाँ है?


27

चुनौती

कोड लिखें, जो एक यादृच्छिक xkcd कॉमिक से एक पैनल की एक छवि दी गई है, एक सत्य मान देता है यदि ब्लैकहॉट कॉमिक या फ़ॉसी में है या नहीं।

ब्लैकहाट कौन है?

ब्लैकहट xkcd कॉमिक्स में पात्र को दिया गया अनौपचारिक नाम है जो एक काली टोपी पहनता है:

कालाहाट पर एक्सप्लेन के एक्सप्लेन पेज से लिया गया

ब्लैकहैट की टोपी हमेशा सीधे पक्षीय, काले रंग की होती है और ऊपर की छवि के समान दिखती है।

अन्य पात्रों में टोपी और बाल भी हो सकते हैं लेकिन किसी के पास टोपी नहीं होगी जो काले और सीधे पक्षीय हैं।

इनपुट

छवि वैसे भी हो सकती है जब आप चाहें कि यह छवि का मार्ग है या STDIN के माध्यम से बाइट्स। आपको इनपुट के रूप में URL लेने की आवश्यकता नहीं है।

नियम

उत्तर को हार्डकोड करने पर प्रतिबंध नहीं है, लेकिन इसकी सराहना नहीं की जाती है।

आपको उत्तर प्राप्त करने के लिए इंटरनेट का उपयोग करने की अनुमति नहीं है।

उदाहरण

सभी चित्र https://xkcd.com से छवियों से काटे गए हैं

ब्लैकहाट पैनल में है (वापसी truthy)


कालाहाट पैनल में नहीं है (वापसी falsey)


टेस्ट बैटरी

जिन 20 चित्रों में ब्लैकहाट है वे यहाँ देखे जा सकते हैं: https://beta-decay.github.io/blackhat.zip

जिन 20 छवियों में ब्लैकहैट नहीं है, उन्हें यहां देखा जा सकता है: https://beta-decay.github.io/no_blackhat.zip

यदि आप अपने कार्यक्रमों का परीक्षण करने के लिए अधिक चित्र चाहते हैं (रहस्य परीक्षण मामलों के लिए प्रशिक्षित करने के लिए), तो आप यहां ब्लैकहाट के सभी दिखावे की सूची पा सकते हैं: http://www.explainxkcd.com/wiki/index.php/Category: Comics_featuring_Black_Hat

जीतना

वह कार्यक्रम जो सही ढंग से पहचानता है कि ब्लैकहैट कॉमिक में है या नहीं, अधिकांश छवियों के लिए जीतता है। आपके हेडर में आपका स्कोर प्रतिशत के रूप में शामिल होना चाहिए।

एक टाईब्रेक की स्थिति में, बंधे हुए कार्यक्रमों को "रहस्य" चित्र दिए जाएंगे (यानी जिनके बारे में केवल मुझे पता है)। जो कोड सबसे सही ढंग से पहचानता है वह टाईब्रेक जीतता है।

रहस्य चित्रों को अंकों के साथ प्रकट किया जाएगा।

नोट: ऐसा लगता है कि रान्डेल का नाम उनके लिए हेट गाइ हो सकता है। मैं ब्लैकहाट को पसंद करता हूं।


12
अगर मैथेमेटिका के पास इसके लिए बिल्ट-इन है तो मुझे आश्चर्य नहीं होगा। ( संदर्भ के लिए )
जे। सल्ल।

5
एक अलग टाई ब्रेकर के लिए सुझाव: छवियों का एक अलग, छोटा सा सेट (5 सच्चे मामलों और 5 झूठ बोलें) जो यहां अप्राप्त हैं, और टाई ब्रेकर का विजेता वह है जो इन अज्ञात छवियों के लिए सबसे अच्छा है। यह उन विशिष्ट छवियों के मुकाबले अधिक सामान्य होशियार समाधानों को प्रोत्साहित करेगा।
सूंदर -

3
पुलिस के साथ और RIAA / MPAA के साथ परीक्षण के मामले सिर्फ बुराई हैं। अच्छा परीक्षण बैटरी, @ BetaDecay।
सूंदर -


1
@ रात 2 क्षमा करें! मैं केवल वहाँ टाई बनाने की योजना बना रहा था। हालांकि 100% पर अच्छा काम!
बीटा

जवाबों:


16

PHP (> = 7), 100% (40/40)

<?php

set_time_limit(0);

class BlackHat
{
    const ROTATION_RANGE = 45;

    private $image;
    private $currentImage;
    private $currentImageWidth;
    private $currentImageHeight;

    public function __construct($path)
    {
        $this->image = imagecreatefrompng($path);
    }

    public function hasBlackHat()
    {
        $angles = [0];

        for ($i = 1; $i <= self::ROTATION_RANGE; $i++) {
            $angles[] = $i;
            $angles[] = -$i;
        }

        foreach ($angles as $angle) {
            if ($angle == 0) {
                $this->currentImage = $this->image;
            } else {
                $this->currentImage = $this->rotate($angle);
            }

            $this->currentImageWidth = imagesx($this->currentImage);
            $this->currentImageHeight = imagesy($this->currentImage);

            if ($this->findBlackHat()) return true;
        }

        return false;
    }

    private function findBlackHat()
    {
        for ($y = 0; $y < $this->currentImageHeight; $y++) {
            for ($x = 0; $x < $this->currentImageWidth; $x++) {
                if ($this->isBlackish($x, $y) && $this->isHat($x, $y)) return true;
            }
        }

        return false;
    }

    private function isHat($x, $y)
    {
        $hatWidth = $this->getBlackishSequenceSize($x, $y, 'right');
        if ($hatWidth < 10) return false;

        $hatHeight = $this->getBlackishSequenceSize($x, $y, 'bottom');

        $hatLeftRim = $hatRightRim = 0;
        for (; ; $hatHeight--) {
            if ($hatHeight < 5) return false;

            $hatLeftRim = $this->getBlackishSequenceSize($x, $y + $hatHeight, 'left');
            if ($hatLeftRim < 3) continue;

            $hatRightRim = $this->getBlackishSequenceSize($x + $hatWidth, $y + $hatHeight, 'right');
            if ($hatRightRim < 2) $hatRightRim = $this->getBlackishSequenceSize($x + $hatWidth, $y + $hatHeight, 'right', 'isLessBlackish');
            if ($hatRightRim < 2) continue;

            break;
        }

        $ratio = $hatWidth / $hatHeight;
        if ($ratio < 2 || $ratio > 4.2) return false;

        $widthRatio = $hatWidth / ($hatLeftRim + $hatRightRim);
        if ($widthRatio < 0.83) return false;
        if ($hatHeight / $hatLeftRim < 1 || $hatHeight / $hatRightRim < 1) return false;

        $pointsScore = 0;
        if ($this->isSurroundedBy($x, $y, 3, true, true, false, false)) $pointsScore++;
        if ($this->isSurroundedBy($x + $hatWidth, $y, 3, true, false, false, true)) $pointsScore++;
        if ($this->isSurroundedBy($x, $y + $hatHeight, 3, false, false, true, false)) $pointsScore++;
        if ($this->isSurroundedBy($x + $hatWidth, $y + $hatHeight, 3, false, false, true, false)) $pointsScore++;
        if ($this->isSurroundedBy($x - $hatLeftRim, $y + $hatHeight, 3, true, true, true, false)) $pointsScore++;
        if ($this->isSurroundedBy($x + $hatWidth + $hatRightRim, $y + $hatHeight, 3, true, false, true, true)) $pointsScore++;
        if ($pointsScore < 3 || ($hatHeight >= 19 && $pointsScore < 4) || ($hatHeight >= 28 && $pointsScore < 5)) return false;

        $middleCheckSize = ($hatHeight >= 15 ? 3 : 2);
        if (!$this->isSurroundedBy($x + (int)($hatWidth / 2), $y, $middleCheckSize, true, null, null, null)) return false;
        if (!$this->isSurroundedBy($x + (int)($hatWidth / 2), $y + $hatHeight, $middleCheckSize, null, null, true, null)) {
            if (!$this->isSurroundedBy($x + (int)(($hatWidth / 4) * 3), $y + $hatHeight, $middleCheckSize, null, null, true, null)) return false;
        }
        if (!$this->isSurroundedBy($x, $y + (int)($hatHeight / 2), $middleCheckSize + 1, null, true, null, null)) return false;
        if (!$this->isSurroundedBy($x + $hatWidth, $y + (int)($hatHeight / 2), $middleCheckSize, null, null, null, true)) return false;

        $badBlacks = 0;
        for ($i = 1; $i <= 3; $i++) {
            if ($y - $i >= 0) {
                if ($this->isBlackish($x, $y - $i)) $badBlacks++;
            }

            if ($x - $i >= 0 && $y - $i >= 0) {
                if ($this->isBlackish($x - $i, $y - $i)) $badBlacks++;
            }
        }
        if ($badBlacks > 2) return false;

        $total = ($hatWidth + 1) * ($hatHeight + 1);
        $blacks = 0;
        for ($i = $x; $i <= $x + $hatWidth; $i++) {
            for ($j = $y; $j <= $y + $hatHeight; $j++) {
                $isBlack = $this->isBlackish($i, $j);
                if ($isBlack) $blacks++;
            }
        }

        if (($total / $blacks > 1.15)) return false;

        return true;
    }

    private function getColor($x, $y)
    {
        return imagecolorsforindex($this->currentImage, imagecolorat($this->currentImage, $x, $y));
    }

    private function isBlackish($x, $y)
    {
        $color = $this->getColor($x, $y);
        return ($color['red'] < 78 && $color['green'] < 78 && $color['blue'] < 78 && $color['alpha'] < 30);
    }

    private function isLessBlackish($x, $y)
    {
        $color = $this->getColor($x, $y);
        return ($color['red'] < 96 && $color['green'] < 96 && $color['blue'] < 96 && $color['alpha'] < 40);
    }

    private function getBlackishSequenceSize($x, $y, $direction, $fn = 'isBlackish')
    {
        $size = 0;

        if ($direction == 'right') {
            for ($x++; ; $x++) {
                if ($x >= $this->currentImageWidth) break;
                if (!$this->$fn($x, $y)) break;
                $size++;
            }
        } elseif ($direction == 'left') {
            for ($x--; ; $x--) {
                if ($x < 0) break;
                if (!$this->$fn($x, $y)) break;
                $size++;
            }
        } elseif ($direction == 'bottom') {
            for ($y++; ; $y++) {
                if ($y >= $this->currentImageHeight) break;
                if (!$this->$fn($x, $y)) break;
                $size++;
            }
        }

        return $size;
    }

    private function isSurroundedBy($x, $y, $size, $top = null, $left = null, $bottom = null, $right = null)
    {
        if ($top !== null) {
            $flag = false;
            for ($i = 1; $i <= $size; $i++) {
                if ($y - $i < 0) break;
                $isBlackish = $this->isBlackish($x, $y - $i);

                if (
                    ($top && !$isBlackish) ||
                    (!$top && $isBlackish)
                ) {
                    $flag = true;
                } elseif ($flag) {
                    return false;
                }
            }
            if (!$flag) return false;
        }

        if ($left !== null) {
            $flag = false;
            for ($i = 1; $i <= $size; $i++) {
                if ($x - $i < 0) break;
                $isBlackish = $this->isBlackish($x - $i, $y);

                if (
                    ($left && !$isBlackish) ||
                    (!$left && $isBlackish)
                ) {
                    $flag = true;
                } elseif ($flag) {
                    return false;
                }
            }
            if (!$flag) return false;
        }

        if ($bottom !== null) {
            $flag = false;
            for ($i = 1; $i <= $size; $i++) {
                if ($y + $i >= $this->currentImageHeight) break;
                $isBlackish = $this->isBlackish($x, $y + $i);

                if (
                    ($bottom && !$isBlackish) ||
                    (!$bottom && $isBlackish)
                ) {
                    $flag = true;
                } elseif ($flag) {
                    return false;
                }
            }
            if (!$flag) return false;
        }

        if ($right !== null) {
            $flag = false;
            for ($i = 1; $i <= $size; $i++) {
                if ($x + $i >= $this->currentImageWidth) break;
                $isBlackish = $this->isBlackish($x + $i, $y);

                if (
                    ($right && !$isBlackish) ||
                    (!$right && $isBlackish)
                ) {
                    $flag = true;
                } elseif ($flag) {
                    return false;
                }
            }
            if (!$flag) return false;
        }

        return true;
    }

    private function rotate($angle)
    {
        return imagerotate($this->image, $angle, imagecolorallocate($this->image, 255, 255, 255));
    }
}

$bh = new BlackHat($argv[1]);
echo $bh->hasBlackHat() ? 'true' : 'false';

इसे चलाने के लिए:

php <filename> <image_path>

उदाहरण:

php black_hat.php "/tmp/blackhat/1.PNG"

टिप्पणियाँ

  • प्रिंट "सच" अगर काली टोपी पाता है और "झूठा" अगर यह नहीं मिलता है।
  • यह PHP के पिछले संस्करणों पर भी काम करना चाहिए, लेकिन सुरक्षित होने के लिए, GD के साथ PHP> = 7 का उपयोग करें ।
  • यह स्क्रिप्ट वास्तव में टोपी को खोजने की कोशिश करती है और ऐसा करने से, यह कई बार छवि को घुमा सकती है और हर बार हजारों और हजारों पिक्सल और सुराग के लिए जांच करती है। तो छवि जितनी बड़ी होगी या जितने अधिक गहरे पिक्सेल होंगे, स्क्रिप्ट खत्म होने में उतना ही अधिक समय लगेगा। यह हालांकि छवियों के बहुमत के लिए एक मिनट के लिए कुछ सेकंड लेना चाहिए।
  • मैं इस स्क्रिप्ट को और अधिक प्रशिक्षित करना पसंद करूंगा, लेकिन मेरे पास ऐसा करने के लिए पर्याप्त समय नहीं है।
  • यह स्क्रिप्ट गोल्फ नहीं है (फिर से क्योंकि मेरे पास पर्याप्त समय नहीं है), लेकिन एक टाई के मामले में गोल्फ में बहुत संभावनाएं हैं।

पता चला काली टोपी के कुछ उदाहरण:

यहाँ छवि विवरण दर्ज करें

इन उदाहरणों को छवि पर पाई गई विशेष बिंदुओं पर लाल रेखाएं खींचकर हासिल किया जाता है, जिसमें तय किया गया है कि स्क्रिप्ट में एक काली टोपी है (चित्र मूल लोगों की तुलना में रोटेशन हो सकते हैं)।


अतिरिक्त

यहां पोस्ट करने से पहले, मैंने इस स्क्रिप्ट को 15 छवियों के एक और सेट के खिलाफ परीक्षण किया, 10 काली टोपी के साथ और 5 काली टोपी के बिना और यह उन सभी के लिए सही हो गया (100%)।

यहाँ जिप फाइल है जिसमें मेरे द्वारा उपयोग की गई अतिरिक्त परीक्षण छवियां हैं: extra.zip

में extra/blackhatनिर्देशिका, लाल लाइनों के साथ पता लगाने के परिणाम भी उपलब्ध हैं। उदाहरण के लिए extra/blackhat/1.pngपरीक्षण छवि है और extra/blackhat/1_r.pngइसका पता लगाने का परिणाम है।


टाईब्रेक कोड गोल्फ नहीं है। इसके बजाय, कार्यक्रमों को छिपे हुए परीक्षण मामलों को खिलाया जाता है जब तक कि टाई ब्रेक हल न हो जाए। फिर मैं आपको परिणाम बताऊंगा और परीक्षण मामलों को पोस्ट करूंगा :)
बीटा डेके

1
@BetaDecay: स्पष्टीकरण के लिए धन्यवाद, यह वाक्य (टाई पर सबसे छोटी जीत) सवाल के पिछले संस्करणों से मेरे सिर में था, इसलिए मैं सोच रहा था कि अगर कोई छिपी हुई परीक्षा के मामलों में होता है, तो सबसे छोटा कोड जीतता है। मेरी गलती!
Night2

7
तुम भी कम से कम होने की संभावना इमेज प्रोसेसिंग भाषा के लिए पुरस्कार जीतने :)
अनुश

@Anush खैर कम से कम PHP में imagerotateअंतर्निहित है, इसलिए ...
user202729

PHP के बारे में मुझे जो पसंद है वह यह है कि इसमें लगभग किसी भी चीज के लिए कुछ बुनियादी कार्यक्षमता है। इसने जीडी को इतने सालों तक बांधा है और जीडी वास्तव में छवियों के साथ काम करने की सबसे आम जरूरतों को पूरा करता है। लेकिन मुझे PHP के बारे में जो कुछ अधिक पसंद है वह यह है कि हमेशा कुछ एक्सटेंशन / पैकेज होते हैं जो आपको अधिक (एक विशाल समुदाय होने के कारण) देंगे। उदाहरण के लिए PHP के लिए OpenCV एक्सटेंशन हैं जो वास्तविक छवि प्रसंस्करण की अनुमति देते हैं!
रात 2

8

मतलाब, 87,5%

function hat=is_blackhat_here2(filepath)

img_hsv = rgb2hsv(imread(filepath));
img_v = img_hsv(:,:,3);

bw = imdilate(imerode( ~im2bw(img_v), strel('disk', 4, 8)), strel('disk', 4, 8));
bw = bwlabel(bw, 8);
bw = imdilate(imerode(bw, strel('disk', 1, 4)), strel('disk', 1, 4));
bw = bwlabel(bw, 4);

region_stats = regionprops(logical(bw), 'all');
hat = false;
for i = 1 : numel(region_stats)
    if mean(img_v(region_stats(i).PixelIdxList)) < 0.15 ...
            && region_stats(i).Area > 30 ...
            && region_stats(i).Solidity > 0.88 ...
            && region_stats(i).Eccentricity > 0.6 ...
            && region_stats(i).Eccentricity < 1 ...
            && abs(region_stats(i).Orientation) < 75...
            && region_stats(i).MinorAxisLength / region_stats(i).MajorAxisLength < 0.5;
        hat = true;
        break;
    end
end

प्रत्याशी क्षेत्रों के आकार में कुछ जाँचों के साथ पिछले संस्करण की वृद्धि।

HAT सेट में वर्गीकरण त्रुटियां : चित्र 4, 14, 15, 17

NON HAT सेट में वर्गीकरण त्रुटियां : चित्र 4

सही वर्गीकृत छवियों के कुछ उदाहरण: यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

गलत वर्गीकृत छवि का उदाहरण:

यहाँ छवि विवरण दर्ज करें

पुराने संस्करण (77,5%)

function hat=is_blackhat_here(filepath)

img_hsv = rgb2hsv(imread(filepath));
img_v = img_hsv(:,:,3);
bw = imerode(~im2bw(img_v), strel('disk', 5, 8));

hat =  mean(img_v(bw)) < 0.04;

इमेज इमोशन के आधार पर दृष्टिकोण, मेमनोनिक द्वारा प्रस्तावित समाधान के समान है, लेकिन एचएसवी छवि के वी चैनल पर आधारित है। इसके अलावा, चयनित क्षेत्र के चैनल का माध्य मान चेक किया गया है (इसका आकार नहीं)।

HAT सेट में वर्गीकरण त्रुटियां : चित्र 4, 5, 10

NON HAT सेट में वर्गीकरण त्रुटियां : चित्र 4, 5, 6, 7, 13, 14


7

पायथ , 62.5%

<214.O.n'z

स्टड पर एक छवि फ़ाइल का फ़ाइल नाम स्वीकार करता है। रिटर्न Trueअगर इसके सभी RGB रंग घटकों का औसत 214 से अधिक है। आप सही पढ़ते हैं: जाहिरा तौर पर ब्लैकहैट छवियां नो-ब्लैकहैट छवियों की तुलना में उज्जवल होती हैं।

(निश्चित रूप से कोई बेहतर कर सकता है — यह !)


2
मुझे पाइथ की शक्ति पर आश्चर्य हुआ जब तक मुझे एहसास नहीं हुआ: डी
बीटा

एक पल के लिए मैंने सोचा "जब से पाइथ को ब्लैकहैट छवियों को पहचानने के लिए बनाया गया है"
लुइस फेलिप डी जीसस मुनोज

2
62.5% 40 छवियों में से 25 है। एक यादृच्छिक अनुमान कार्यक्रम (निश्चित बीज के साथ, कुछ ऐसा) जो का एक संभाव्यता होगा कम से कम उतना अच्छा कर रहे हैं। i=2540(40i)2407.7%
user202729

6

अजगर 2, 65% 72.5% 77.5% (= 31/40)

import cv2
import numpy as np
from scipy import misc

def blackhat(path):
    im = misc.imread(path)
    black = (im[:, :, 0] < 10) & (im[:, :, 1] < 10) & (im[:, :, 2] < 10)
    black = black.astype(np.ubyte)

    black = cv2.erode(black, np.ones((3, 3)), iterations=3)

    return 5 < np.sum(black) < 2000

यह आंकड़े बताते हैं कि कौन से पिक्सेल काले हैं, फिर छोटे सन्निहित टुकड़ों को मिटा देते हैं। निश्चित रूप से यहां सुधार के लिए जगह है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.