МАТЕМАТИКА: Интеллектуальное определение ЦВЕТА товара-одежды при загрузке фотографии

Рейтинг: 4.5447  
На голосовании
Предложил Роман Забродин 15.05.2015 06:46:11

МАТЕМАТИКА: Интеллектуальное определение ЦВЕТА товара-одежды при загрузке фотографии

Цвет очень нужен для ФИЛЬТРА. Проставлять его для десятков тысяч SKU -- это рутина, лучше написать АЛГОРИТМ! Многие и-м это УЖЕ используют...

Рейтинг: 24.8177  
Максим Смирнов 15.05.2015 11:11:22
не МАТЕМАТИКА, а БИОЛОГИЯ ;)
Рейтинг: 0  
Роман Забродин 15.05.2015 11:41:36
Ну будет 1% ошибок -- мелочь :)
Рейтинг: 0  
Роман Забродин 15.05.2015 11:41:53
Ну будет 1% ошибок -- мелочь :)
Рейтинг: 5.623  
Максим Смирнов 15.05.2015 12:57:05
не знаю как ЭТО прикрутить к интерфейсу.
и ОНО сбоит на некоторых картинках.
потестируй. расскажи о впечатлениях, плиз.

надо добавить поля "Число" в хайлоадблок UF_COLOR_R, UF_COLOR_G и UF_COLOR_B
алгоритм их заполнит при первом запуске.

$picture = $_SERVER["DOCUMENT_ROOT"]."/upload/iblock/c89/c89bdc958fdbe9010394307fc83386e2.jpg";
$highloadBlock = 'eshop_color_reference';

CModule::IncludeModule('highloadblock');
CModule::IncludeModule('iblock');

$hlblock = Bitrix\Highloadblock\HighloadBlockTable::getList(array(
   "filter" => array(
      "=TABLE_NAME" => $highloadBlock,
   )))->fetch();
$entity = Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlblock);
$entity_data_class = $entity->getDataClass();
$rsData = $entity_data_class::getList(array(
   "select" => array("ID", "UF_NAME", "UF_FILE", "UF_COLOR_R", "UF_COLOR_G", "UF_COLOR_B"),
));
$colorReference = array();
while($arData = $rsData->fetch())
{
   if (
      $arData["UF_FILE"]
      && (
         !isset($arData["UF_COLOR_R"])
         || !isset($arData["UF_COLOR_G"])
         || !isset($arData["UF_COLOR_B"])
      )
   )
   {
      $arFile = CFile::MakeFileArray($arData["UF_FILE"]);
      if ($arFile && CFile::ResizeImage($arFile, array("width"=>40,"height"=>40)))
      {
         $color = getPictureColor($arFile["tmp_name"], 10);
         $arData["UF_COLOR_R"] = $color["R"];
         $arData["UF_COLOR_G"] = $color["G"];
         $arData["UF_COLOR_B"] = $color["B"];
         $entity_data_class::update($arData["ID"], array(
            "UF_COLOR_R" => $color["R"],
            "UF_COLOR_G" => $color["G"],
            "UF_COLOR_B" => $color["B"],
         ));
      }
   }
   $colorReference[] = $arData;
}

$arFile = CFile::MakeFileArray($picture);
if ($arFile && CFile::ResizeImage($arFile, array("width"=>40,"height"=>40)))
{
   $color = getPictureColor($arFile["tmp_name"], 10);
   foreach ($colorReference as &$arData)
   {
      $dR = $arData["UF_COLOR_R"] - $color["R"];
      $dG = $arData["UF_COLOR_G"] - $color["G"];
      $dB = $arData["UF_COLOR_B"] - $color["B"];
      $distance = $dR*$dR + $dG*$dG + $dB*$dB;
      $arData["DISTANCE"] = $distance;
   }
   unset($arData);
}
\Bitrix\Main\Type\Collection::sortByColumn($colorReference, array("DISTANCE" => SORT_ASC));
var_dump(current($colorReference));

function getPictureColor($path, $border = 0)
{
   $result = array(
      "R" => 0,
      "G" => 0,
      "B" => 0,
   );
   $image = CFile::CreateImage($path);
   $width = imagesx($image);
   $height = imagesy($image);
   $c = 0;
   for ($x = $border; $x < $width-$border; $x++)
   {
      for ($y = $border; $y < $height-$border; $y++)
      {
         $c++;
         $rgb = imagecolorat($image, $x, $y);
         $result["R"] += (($rgb >> 16) & 0xFF);
         $result["G"] += (($rgb >> 8) & 0xFF);
         $result["B"] += ($rgb & 0xFF);
      }
   }

   if ($c)
   {
      foreach ($result as $i => $s)
      {
         $result[$i] = $s/$c;
      }
   }

   return $result;
}
 
Рейтинг: 0  
Максим Смирнов 15.05.2015 13:18:19
Нужно добавить больше CIE L*a*b* ))
Скоро будет лучший вариант
Рейтинг: 0.0864  
Максим Смирнов 15.05.2015 13:48:17
Гораздо более лучшая версия (ещё есть что улучшить):

$picture = $_SERVER["DOCUMENT_ROOT"]."/upload/iblock/0d9/0d922f208991da15997032f1bb80b0f8.jpg";
$highloadBlock = 'eshop_color_reference';

CModule::IncludeModule('highloadblock');
CModule::IncludeModule('iblock');

$hlblock = Bitrix\Highloadblock\HighloadBlockTable::getList(array(
   "filter" => array(
      "=TABLE_NAME" => $highloadBlock,
   )))->fetch();
$entity = Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlblock);
$entity_data_class = $entity->getDataClass();
$rsData = $entity_data_class::getList(array(
   "select" => array("ID", "UF_NAME", "UF_FILE", "UF_COLOR_R", "UF_COLOR_G", "UF_COLOR_B"),
));
$colorReference = array();
while($arData = $rsData->fetch())
{
   if (
      $arData["UF_FILE"]
      && (
         !isset($arData["UF_COLOR_R"])
         || !isset($arData["UF_COLOR_G"])
         || !isset($arData["UF_COLOR_B"])
      )
   )
   {
      $arFile = CFile::MakeFileArray($arData["UF_FILE"]);
      if ($arFile && CFile::ResizeImage($arFile, array("width"=>40,"height"=>40)))
      {
         $color = getPictureColor($arFile["tmp_name"], 10);
         $arData["UF_COLOR_R"] = $color["R"];
         $arData["UF_COLOR_G"] = $color["G"];
         $arData["UF_COLOR_B"] = $color["B"];
         $entity_data_class::update($arData["ID"], array(
            "UF_COLOR_R" => $color["R"],
            "UF_COLOR_G" => $color["G"],
            "UF_COLOR_B" => $color["B"],
         ));
      }
   }
   $colorReference[] = $arData;
}

$arFile = CFile::MakeFileArray($picture);
if ($arFile && CFile::ResizeImage($arFile, array("width"=>40,"height"=>40), BX_RESIZE_IMAGE_EXACT))
{
   copy($arFile["tmp_name"], $_SERVER["DOCUMENT_ROOT"]."/test.jpg");
   echo '<img src="/test.jpg?'.rand(0,1000000).'"><br>';
   $color = getPictureColor($arFile["tmp_name"], 10);
   var_dump($color);echo "<br>";
   foreach ($colorReference as &$arData)
   {
      $c1 = rgb2xyz(array("R"=>$arData["UF_COLOR_R"],"G"=>$arData["UF_COLOR_G"],"B"=>$arData["UF_COLOR_B"]));
      $c1 = xyz2lab($c1);
      
      $c2 = rgb2xyz($color);
      $c2 = xyz2lab($c2);

      $dL = $c1["L"] - $c2["L"];
      $dA = $c1["a"] - $c2["a"];
      $dB = $c1["b"] - $c2["b"];
      $distance = $dL*$dL + $dA*$dA + $dB*$dB;

      $arData["DISTANCE"] = $distance;
      echo $distance,"<br>";
   }
   unset($arData);
}
\Bitrix\Main\Type\Collection::sortByColumn($colorReference, array("DISTANCE" => SORT_ASC));
var_dump(current($colorReference));

function getPictureColor($path, $border = 0)
{
   $result = array(
      "R" => 0,
      "G" => 0,
      "B" => 0,
   );
   $image = CFile::CreateImage($path);
   $width = imagesx($image);
   $height = imagesy($image);
   $c = 0;
   for ($x = $border; $x < $width-$border; $x++)
   {
      for ($y = $border; $y < $height-$border; $y++)
      {
         $c++;
         $rgb = imagecolorat($image, $x, $y);
         $result["R"] += (($rgb >> 16) & 0xFF);
         $result["G"] += (($rgb >> 8) & 0xFF);
         $result["B"] += ($rgb & 0xFF);
      }
   }

   if ($c)
   {
      foreach ($result as $i => $s)
      {
         $result[$i] = $s/$c;
      }
   }

   return $result;
}

function rgb2xyz($color)
{
   $var_R = ( $color["R"] / 255 );        //R from 0 to 255
   $var_G = ( $color["G"] / 255 );        //G from 0 to 255
   $var_B = ( $color["B"] / 255 );        //B from 0 to 255

   if ($var_R > 0.04045)
      $var_R = pow(($var_R + 0.055) / 1.055, 2.4);
   else
      $var_R = $var_R / 12.92;

   if ($var_G > 0.04045)
      $var_G = pow(($var_G + 0.055) / 1.055, 2.4);
   else
      $var_G = $var_G / 12.92;

   if ($var_B > 0.04045)
      $var_B = pow(($var_B + 0.055) / 1.055, 2.4);
   else
      $var_B = $var_B / 12.92;

   $var_R = $var_R * 100;
   $var_G = $var_G * 100;
   $var_B = $var_B * 100;

   //Observer. = 2°, Illuminant = D65
   $result = array(
      "X" => $var_R * 0.4124 + $var_G * 0.3576 + $var_B * 0.1805,
      "Y" => $var_R * 0.2126 + $var_G * 0.7152 + $var_B * 0.0722,
      "Z" => $var_R * 0.0193 + $var_G * 0.1192 + $var_B * 0.9505,
   );
   return $result;
}
function xyz2lab($color)
{
   //Observer= 2°, Illuminant= D65
   $var_X = $color["X"] / 95.047;
   $var_Y = $color["Y"] / 100.000;
   $var_Z = $color["Z"] / 108.883;

   if ($var_X > 0.008856)
      $var_X = pow($var_X, 1/3);
   else
      $var_X = (7.787 * $var_X) + (16 / 116);

   if ($var_Y > 0.008856)
      $var_Y = pow($var_Y, 1/3);
   else
      $var_Y = (7.787 * $var_Y) + (16 / 116);

   if ($var_Z > 0.008856)
      $var_Z = pow($var_Z, 1/3);
   else
      $var_Z = (7.787 * $var_Z) + (16 / 116);

   $result = array(
      "L" => ( 116 * $var_Y ) - 16,
      "a" => 500 * ($var_X - $var_Y),
      "b" => 200 * ($var_Y - $var_Z),
   );
   return $result;
}
 
Рейтинг: 11.5448  
Максим Смирнов 18.05.2015 16:54:27
Рейтинг: 0  
Роман Забродин 29.04.2016 09:11:37
Реализовали описанный функционал в нашем типовом решении Битроник 2 PRO, подробнее в блоге:
http://romza.ru/blog/romza/bitronik-2160/