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';
Çalıştırmak için:
php <filename> <image_path>
Örnek:
php black_hat.php "/tmp/blackhat/1.PNG"
notlar
- Siyah şapka bulursa "true", bulamazsa "false" yazar.
- Bu PHP'nin önceki sürümlerinde de çalışmalı, ancak güvenli olması için GD ile PHP> = 7 kullanmalısınız .
- Bu senaryo aslında şapkayı bulmaya çalışır ve bunu yaparak görüntüyü birçok kez döndürebilir ve her seferinde binlerce ve binlerce piksel ve ipucunu kontrol eder. Bu yüzden, görüntü ne kadar büyük olursa veya sahip olduğu daha karanlık pikseller için, komut dosyasının tamamlanması daha uzun sürer. Yine de görüntülerin çoğunluğu için birkaç dakika ile bir dakika arasında sürebilir.
- Bu senaryoyu daha fazla eğitmeyi çok isterdim, ancak yapacak vaktim yok.
- Bu senaryo golf oynamıyor (yine de yeterli zamanım olmadığından), ancak beraberlik durumunda golf oynama potansiyeli çok fazla.
Tespit edilen siyah şapkalardan bazı örnekler:
Bu örnekler, betiğin siyah bir şapkaya sahip olduğuna karar verilen görüntünün üzerinde bulunan özel noktalara kırmızı çizgiler çizerek elde edilir (görüntüler orijinal olanlara göre dönebilir).
Ekstra
Buraya yazmadan önce, bu senaryoyu, 10'u siyah şapkalı ve 5'i siyah şapkalı olmayan 15 resimden oluşan bir gruba karşı test ettim ve hepsi için de doğru gitti (% 100).
Kullandığım ekstra test resimlerini içeren ZIP dosyası:
Olarak extra/blackhat
dizin kırmızı çizgi ile tespit sonuçları da mevcuttur. Örneğin extra/blackhat/1.png
, test görüntüsüdür ve extra/blackhat/1_r.png
bunun tespit sonucudur.