- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我想比较下图的相似度。根据我的要求,我想将所有这些图像识别为相似,因为它使用相同的颜色、相同的剪贴画。这些图像的唯一区别是旋转、缩放和剪贴画的位置。由于所有 3 件 T 恤都使用了相同的颜色和剪贴画,我想将所有 3 个图像识别为相似。我尝试了hackerfactor.com中描述的方法.但是根据我的要求,它并没有给我正确的结果。如何将所有这些图像识别为相似?您有什么建议吗?请帮帮我。
下面的图片应该被识别为与上面的图片不同。(即使T恤颜色相同,剪贴画也不同。最后一件T恤与上面不同,因为它使用了相同的剪贴画,但是两次。)
最佳答案
因为这个问题很有趣,所以我把整个问题都移到了 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 恤:
$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 实现。您现在可以创建一个新图像并减去它的一些蒙版以消除背景。然后您可以使用比较方法将一张图像与另一张图像进行比较。为了限制计算,最好先调整图像大小( 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/
我在 JavaScript 文件中运行 PHP,例如...... var = '';). 我需要使用 JavaScript 来扫描字符串中的 PHP 定界符(打开和关闭 PHP 的 )。 我已经知道使
我希望能够做这样的事情: php --determine-oldest-supported-php-version test.php 并得到这个输出: 7.2 也就是说,php 二进制检查 test.
我正在开发一个目前不使用任何框架的大型 php 站点。我的大问题是,随着时间的推移慢慢尝试将框架融入应用程序是否可取,例如在创建的新部件和更新的旧部件中? 比如所有的页面都是直接通过url服务的,有几
下面是我的源代码,我想在同一页面顶部的另一个 php 脚本中使用位于底部 php 脚本的变量 $r1。我需要一个简单的解决方案来解决这个问题。我想在代码中存在的更新查询中使用该变量。 $name)
我正在制作一个网站,根据不同的情况进行大量 PHP 重定向。就像这样...... header("Location: somesite.com/redirectedpage.php"); 为了安全起见
我有一个旧网站,我的 php 标签从 因为短标签已经显示出安全问题,并且在未来的版本中将不被支持。 关于php - 如何避免在 php 文件中写入
我有一个用 PHP 编写的配置文件,如下所示, 所以我想用PHP开发一个接口(interface),它可以编辑文件值,如$WEBPATH , $ACCOUNTPATH和 const值(value)观
我试图制作一个登录页面来学习基本的PHP,首先我希望我的独立PHP文件存储HTML文件的输入(带有表单),但是当我按下按钮时(触发POST到PHP脚本) )我一直收到令人不愉快的错误。 我已经搜索了S
我正在寻找一种让 PHP 以一种形式打印任意数组的方法,我可以将该数组作为赋值包含在我的(测试)代码中。 print_r 产生例如: Array ( [0] => qsr-part:1285 [1]
这个问题已经有答案了: 已关闭11 年前。 Possible Duplicate: What is the max key size for an array in PHP? 正如标题所说,我想知道
我正在寻找一种让 PHP 以一种形式打印任意数组的方法,我可以将该数组作为赋值包含在我的(测试)代码中。 print_r 产生例如: Array ( [0] => qsr-part:1285 [1]
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 9 年前。 Improve this ques
我在 MySQL 数据库中有一个表,其中存储餐厅在每个工作日和时段提供的菜单。 表结构如下: i_type i_name i_cost i_day i_start i_
我有两页。 test1.php 和 test2.php。 我想做的就是在 test1.php 上点击提交,并将 test2.php 显示在 div 中。这实际上工作正常,但我需要向 test2.php
我得到了这个代码。我想通过textarea更新mysql。我在textarea中回显我的MySQL,但我不知道如何更新它,我应该把所有东西都放进去吗,因为_GET模式没有给我任何东西,我也尝试_GET
首先,我是 php 的新手,所以我仍在努力学习。我在 Wordpress 上创建了一个表单,我想将值插入一个表(data_test 表,我已经管理了),然后从 data_test 表中获取所有列(id
我有以下函数可以清理用户或网址的输入: function SanitizeString($var) { $var=stripslashes($var); $va
我有一个 html 页面,它使用 php 文件查询数据库,然后让用户登录,否则拒绝访问。我遇到的问题是它只是重定向到 php 文件的 url,并且从不对发生的事情提供反馈。这是我第一次使用 html、
我有一个页面充满了指向 pdf 的链接,我想跟踪哪些链接被单击。我以为我可以做如下的事情,但遇到了问题: query($sql); if($result){
我正在使用 从外部文本文件加载 HTML/PHP 代码 $f = fopen($filename, "r"); while ($line = fgets($f, 4096)) { print $l
我是一名优秀的程序员,十分优秀!