gpt4 book ai didi

php - 如何使用 php 比较图像相似度而不考虑比例、旋转?

转载 作者:IT王子 更新时间:2023-10-29 00:03:40 24 4
gpt4 key购买 nike

我想比较下图的相似度。根据我的要求,我想将所有这些图像识别为相似,因为它使用相同的颜色、相同的剪贴画。这些图像的唯一区别是旋转、缩放和剪贴画的位置。由于所有 3 件 T 恤都使用了相同的颜色和剪贴画,我想将所有 3 个图像识别为相似。我尝试了hackerfactor.com中描述的方法.但是根据我的要求,它并没有给我正确的结果。如何将所有这些图像识别为相似?您有什么建议吗?请帮帮我。

enter image description here enter image description here enter image description here

下面的图片应该被识别为与上面的图片不同。(即使T恤颜色相同,剪贴画也不同。最后一件T恤与上面不同,因为它使用了相同的剪贴画,但是两次。)

Image A Image B Image C

最佳答案

已移至 GitHub

因为这个问题很有趣,所以我把整个问题都移到了 GitHub 上,在那里你可以找到当前的实现: ImageCompare

原始答案

我采用了一种非常简单的方法,使用 img-resize 并比较调整大小后的图像的平均颜色。

$binEqual = [
file_get_contents('http://i.stack.imgur.com/D8ct1.png'),
file_get_contents('http://i.stack.imgur.com/xNZt1.png'),
file_get_contents('http://i.stack.imgur.com/kjGjm.png')
];

$binDiff = [
file_get_contents('http://i.stack.imgur.com/WIOHs.png'),
file_get_contents('http://i.stack.imgur.com/ljoBT.png'),
file_get_contents('http://i.stack.imgur.com/qEKSK.png')
];


function getAvgColor($bin, $size = 10) {

$target = imagecreatetruecolor($size, $size);
$source = imagecreatefromstring($bin);

imagecopyresized($target, $source, 0, 0, 0, 0, $size, $size, imagesx($source), imagesy($source));

$r = $g = $b = 0;

foreach(range(0, $size - 1) as $x) {
foreach(range(0, $size - 1) as $y) {
$rgb = imagecolorat($target, $x, $y);
$r += $rgb >> 16;
$g += $rgb >> 8 & 255;
$b += $rgb & 255;
}
}

unset($source, $target);

return (floor($r / $size ** 2) << 16) + (floor($g / $size ** 2) << 8) + floor($b / $size ** 2);
}

function compAvgColor($c1, $c2, $tolerance = 4) {

return abs(($c1 >> 16) - ($c2 >> 16)) <= $tolerance &&
abs(($c1 >> 8 & 255) - ($c2 >> 8 & 255)) <= $tolerance &&
abs(($c1 & 255) - ($c2 & 255)) <= $tolerance;
}

$perms = [[0,1],[0,2],[1,2]];

foreach($perms as $perm) {
var_dump(compAvgColor(getAvgColor($binEqual[$perm[0]]), getAvgColor($binEqual[$perm[1]])));
}

foreach($perms as $perm) {
var_dump(compAvgColor(getAvgColor($binDiff[$perm[0]]), getAvgColor($binDiff[$perm[1]])));
}

对于使用的尺寸和颜色公差,我得到了预期的结果:

bool(true)
bool(true)
bool(true)
bool(false)
bool(false)
bool(false)

更高级的实现

要比较的空 T 恤: Plain T-Shirt

$binEqual = [
file_get_contents('http://i.stack.imgur.com/D8ct1.png'),
file_get_contents('http://i.stack.imgur.com/xNZt1.png'),
file_get_contents('http://i.stack.imgur.com/kjGjm.png')
];

$binDiff = [
file_get_contents('http://i.stack.imgur.com/WIOHs.png'),
file_get_contents('http://i.stack.imgur.com/ljoBT.png'),
file_get_contents('http://i.stack.imgur.com/qEKSK.png')
];

class Color {
private $r = 0;
private $g = 0;
private $b = 0;

public function __construct($r = 0, $g = 0, $b = 0)
{
$this->r = $r;
$this->g = $g;
$this->b = $b;
}

public function r()
{
return $this->r;
}

public function g()
{
return $this->g;
}

public function b()
{
return $this->b;
}

public function toInt()
{
return $this->r << 16 + $this->g << 8 + $this->b;
}

public function toRgb()
{
return [$this->r, $this->g, $this->b];
}

public function mix(Color $color)
{
$this->r = round($this->r + $color->r() / 2);
$this->g = round($this->g + $color->g() / 2);
$this->b = round($this->b + $color->b() / 2);
}

public function compare(Color $color, $tolerance = 500)
{
list($r1, $g1, $b1) = $this->toRgb();
list($r2, $g2, $b2) = $color->toRgb();

$diff = round(sqrt(pow($r1 - $r2, 2) + pow($g1 - $g2, 2) + pow($b1 - $b2, 2)));

printf("Comp r(%s : %s), g(%s : %s), b(%s : %s) Diff %s \n", $r1, $r2, $g1, $g2, $b1, $b2, $diff);

return $diff <= $tolerance;
}

public static function fromInt($int) {
return new self($int >> 16, $int >> 8 & 255, $int & 255);
}
}


function getAvgColor($bin, $size = 5) {

$target = imagecreatetruecolor($size, $size);
$targetTmp = imagecreatetruecolor($size, $size);

$sourceTmp = imagecreatefrompng('http://i.stack.imgur.com/gfn5A.png');
$source = imagecreatefromstring($bin);

imagecopyresized($target, $source, 0, 0, 0, 0, $size, $size, imagesx($source), imagesy($source));
imagecopyresized($targetTmp, $sourceTmp, 0, 0, 0, 0, $size, $size, imagesx($source), imagesy($source));

$r = $g = $b = $relPx = 0;

$baseColor = new Color();

foreach(range(0, $size - 1) as $x) {
foreach(range(0, $size - 1) as $y) {
if (imagecolorat($target, $x, $y) != imagecolorat($targetTmp, $x, $y))
$baseColor->mix(Color::fromInt(imagecolorat($target, $x, $y)));
}
}

unset($source, $target, $sourceTmp, $targetTmp);

return $baseColor;

}

$perms = [[0,0], [1,0], [2,0], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]];

echo "Equal\n";
foreach($perms as $perm) {
var_dump(getAvgColor($binEqual[$perm[0]])->compare(getAvgColor($binEqual[$perm[1]])));
}

echo "Different\n";
foreach($perms as $perm) {
var_dump(getAvgColor($binEqual[$perm[0]])->compare(getAvgColor($binDiff[$perm[1]])));
}

结果:

Equal
Comp r(101 : 101), g(46 : 46), b(106 : 106) Diff 0
bool(true)
Comp r(121 : 101), g(173 : 46), b(249 : 106) Diff 192
bool(true)
Comp r(219 : 101), g(179 : 46), b(268 : 106) Diff 241
bool(true)
Comp r(121 : 101), g(173 : 46), b(249 : 106) Diff 192
bool(true)
Comp r(121 : 121), g(173 : 173), b(249 : 249) Diff 0
bool(true)
Comp r(121 : 219), g(173 : 179), b(249 : 268) Diff 100
bool(true)
Comp r(219 : 101), g(179 : 46), b(268 : 106) Diff 241
bool(true)
Comp r(219 : 121), g(179 : 173), b(268 : 249) Diff 100
bool(true)
Comp r(219 : 219), g(179 : 179), b(268 : 268) Diff 0
bool(true)
Different
Comp r(101 : 446), g(46 : 865), b(106 : 1242) Diff 1442
bool(false)
Comp r(121 : 446), g(173 : 865), b(249 : 1242) Diff 1253
bool(false)
Comp r(219 : 446), g(179 : 865), b(268 : 1242) Diff 1213
bool(false)
Comp r(121 : 446), g(173 : 865), b(249 : 1242) Diff 1253
bool(false)
Comp r(121 : 654), g(173 : 768), b(249 : 1180) Diff 1227
bool(false)
Comp r(121 : 708), g(173 : 748), b(249 : 1059) Diff 1154
bool(false)
Comp r(219 : 446), g(179 : 865), b(268 : 1242) Diff 1213
bool(false)
Comp r(219 : 654), g(179 : 768), b(268 : 1180) Diff 1170
bool(false)
Comp r(219 : 708), g(179 : 748), b(268 : 1059) Diff 1090
bool(false)

在此计算中,背景被忽略,导致平均颜色差异较大。

最终实现(OOP)

非常有趣的话题。所以我试着把它调高一点。现在这是一个完整的 OOP 实现。您现在可以创建一个新图像并减去它的一些蒙版以消除背景。然后您可以使用比较方法将一张图像与另一张图像进行比较。为了限制计算,最好先调整图像大小( mask 总是适合当前图像)

比较算法将两个图像自行分 block 为多个图 block ,然后消除几乎等于白色平均颜色的图 block ,然后比较所有剩余图 block 排列的平均颜色。

Class Image {

const HASH_SIZE = 8;
const AVG_SIZE = 10;

private $img = null;

public function __construct($resource)
{
$this->img = $resource;;
}

private function permute(array $a1, array $a2) {
$perms = array();
for($i = 0; $i < sizeof($a1); $i++) {
for($j = $i; $j < sizeof($a2); $j++) {
if ($i != $j) {
$perms[] = [$a1[$i],
$a2[$j]];
}
}
}

return $perms;
}

public function compare(Image $comp) {
$avgComp = array();

foreach($comp->chunk(25) as $chunk) {
$avgComp[] = $chunk->avg();
}

$avgOrg = array();

foreach($this->chunk(25) as $chunk) {
$avgOrg[] = $chunk->avg();
}

$white = Color::fromInt(0xFFFFFF);

$avgComp = array_values(array_filter($avgComp, function(Color $color) use ($white){
return $white->compare($color, 1000);
}));

$avgOrg = array_values(array_filter($avgOrg, function(Color $color) use ($white){
return $white->compare($color, 1000);
}));

$equal = 0;
$pairs = $this->permute($avgOrg, $avgComp);

foreach($pairs as $pair) {
$equal += $pair[0]->compare($pair[1], 100) ? 1 : 0;
}

return ($equal / sizeof($pairs));
}

public function substract(Image $mask, $tolerance = 50)
{
$size = $this->size();

if ($mask->size() != $size) {
$mask = $mask->resize($size);
}

for ($x = 0; $x < $size[0]; $x++) {
for ($y = 0; $y < $size[1]; $y++) {
if ($this->colorat($x, $y)->compare($mask->colorat($x, $y), $tolerance))
imagesetpixel($this->img, $x, $y, 0xFFFFFF);
}
}

return $this;
}

public function avg($size = 10)
{
$target = $this->resize([self::AVG_SIZE, self::AVG_SIZE]);

$avg = Color::fromInt(0x000000);
$white = Color::fromInt(0xFFFFFF);

for ($x = 0; $x < self::AVG_SIZE; $x++) {
for ($y = 0; $y < self::AVG_SIZE; $y++) {
$color = $target->colorat($x, $y);
if (!$color->compare($white, 10))
$avg->mix($color);
}
}

return $avg;
}

public function colorat($x, $y)
{
return Color::fromInt(imagecolorat($this->img, $x, $y));
}

public function chunk($chunkSize = 10)
{
$collection = new ImageCollection();
$size = $this->size();

for($x = 0; $x < $size[0]; $x += $chunkSize) {
for($y = 0; $y < $size[1]; $y += $chunkSize) {
switch (true) {
case ($x + $chunkSize > $size[0] && $y + $chunkSize > $size[1]):
$collection->push($this->slice(['x' => $x, 'y' => $y, 'height' => $size[0] - $x, 'width' => $size[1] - $y]));
break;
case ($x + $chunkSize > $size[0]):
$collection->push($this->slice(['x' => $x, 'y' => $y, 'height' => $size[0] - $x, 'width' => $chunkSize]));
break;
case ($y + $chunkSize > $size[1]):
$collection->push($this->slice(['x' => $x, 'y' => $y, 'height' => $chunkSize, 'width' => $size[1] - $y]));
break;
default:
$collection->push($this->slice(['x' => $x, 'y' => $y, 'height' => $chunkSize, 'width' => $chunkSize]));
break;
}
}
}

return $collection;
}

public function slice(array $rect)
{
return Image::fromResource(imagecrop($this->img, $rect));
}

public function size()
{
return [imagesx($this->img), imagesy($this->img)];
}

public function resize(array $size = array(100, 100))
{
$target = imagecreatetruecolor($size[0], $size[1]);
imagecopyresized($target, $this->img, 0, 0, 0, 0, $size[0], $size[1], imagesx($this->img), imagesy($this->img));

return Image::fromResource($target);
}

public function show()
{
header("Content-type: image/png");
imagepng($this->img);
die();
}

public function save($name = null, $path = '') {
if ($name === null) {
$name = $this->hash();
}

imagepng($this->img, $path . $name . '.png');

return $this;
}

public function hash()
{
// Resize the image.
$resized = imagecreatetruecolor(self::HASH_SIZE, self::HASH_SIZE);
imagecopyresampled($resized, $this->img, 0, 0, 0, 0, self::HASH_SIZE, self::HASH_SIZE, imagesx($this->img), imagesy($this->img));
// Create an array of greyscale pixel values.
$pixels = [];
for ($y = 0; $y < self::HASH_SIZE; $y++)
{
for ($x = 0; $x < self::HASH_SIZE; $x++)
{
$rgb = imagecolorsforindex($resized, imagecolorat($resized, $x, $y));
$pixels[] = floor(($rgb['red'] + $rgb['green'] + $rgb['blue']) / 3);
}
}
// Free up memory.
imagedestroy($resized);
// Get the average pixel value.
$average = floor(array_sum($pixels) / count($pixels));
// Each hash bit is set based on whether the current pixels value is above or below the average.
$hash = 0; $one = 1;
foreach ($pixels as $pixel)
{
if ($pixel > $average) $hash |= $one;
$one = $one << 1;
}
return $hash;
}

public static function fromResource($resource)
{
return new self($resource);
}

public static function fromBin($binf)
{
return new self(imagecreatefromstring($bin));
}

public static function fromFile($path)
{
return new self(imagecreatefromstring(file_get_contents($path)));
}
}

class ImageCollection implements IteratorAggregate
{
private $images = array();

public function __construct(array $images = array())
{
$this->images = $images;
}

public function push(Image $image) {
$this->images[] = $image;
return $this;
}

public function pop()
{
return array_pop($this->images);
}

public function save()
{
foreach($this->images as $image)
{
$image->save();
}

return $this;
}

public function getIterator() {
return new ArrayIterator($this->images);
}
}

class Color {
private $r = 0;
private $g = 0;
private $b = 0;

public function __construct($r = 0, $g = 0, $b = 0)
{
$this->r = $r;
$this->g = $g;
$this->b = $b;
}

public function r()
{
return $this->r;
}

public function g()
{
return $this->g;
}

public function b()
{
return $this->b;
}

public function toInt()
{
return $this->r << 16 + $this->g << 8 + $this->b;
}

public function toRgb()
{
return [$this->r, $this->g, $this->b];
}

public function mix(Color $color)
{
$this->r = round($this->r + $color->r() / 2);
$this->g = round($this->g + $color->g() / 2);
$this->b = round($this->b + $color->b() / 2);
}

public function compare(Color $color, $tolerance = 500)
{
list($r1, $g1, $b1) = $this->toRgb();
list($r2, $g2, $b2) = $color->toRgb();

$diff = round(sqrt(pow($r1 - $r2, 2) + pow($g1 - $g2, 2) + pow($b1 - $b2, 2)));

//printf("Comp r(%s : %s), g(%s : %s), b(%s : %s) Diff %s \n", $r1, $r2, $g1, $g2, $b1, $b2, $diff);

return $diff <= $tolerance;
}

public static function fromInt($int) {
return new self($int >> 16, $int >> 8 & 255, $int & 255);
}
}

$mask = Image::fromFile('http://i.stack.imgur.com/gfn5A.png');

$image1 = Image::fromFile('http://i.stack.imgur.com/D8ct1.png')->resize([50, 100])->substract($mask, 100);
$image2 = Image::fromFile('http://i.stack.imgur.com/xNZt1.png')->resize([50, 100])->substract($mask, 100);
$image3 = Image::fromFile('http://i.stack.imgur.com/kjGjm.png')->resize([50, 100])->substract($mask, 100);

$other1 = Image::fromFile('http://i.stack.imgur.com/WIOHs.png')->resize([50, 100])->substract($mask, 100);
$other2 = Image::fromFile('http://i.stack.imgur.com/ljoBT.png')->resize([50, 100])->substract($mask, 100);
$other3 = Image::fromFile('http://i.stack.imgur.com/qEKSK.png')->resize([50, 100])->substract($mask, 100);


echo "Equal\n";
var_dump(
$image1->compare($image2),
$image1->compare($image3),
$image2->compare($image3)
);

echo "Image 1 to Other\n";
var_dump(
$image1->compare($other1),
$image1->compare($other2),
$image1->compare($other3)
);

echo "Image 2 to Other\n";
var_dump(
$image2->compare($other1),
$image2->compare($other2),
$image2->compare($other3)
);

echo "Image 3 to Other\n";
var_dump(
$image3->compare($other1),
$image3->compare($other2),
$image3->compare($other3)
);

结果:

Equal
float(0.47619047619048)
float(0.53333333333333)
float(0.4)
Image 1 to Other
int(0)
int(0)
int(0)
Image 2 to Other
int(0)
int(0)
int(0)
Image 3 to Other
int(0)
int(0)
int(0)

关于php - 如何使用 php 比较图像相似度而不考虑比例、旋转?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31261456/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com