воскресенье, 2 августа 2015 г.

Тестируем матрицу зеркального фотоаппарата - PSD и PHP

   Выкладываю обещанный скрипт для проверки наличия битых пикселей на матрице. Да кому он нужен, когда можно глазками все посмотреть? Он может пригодиться при покупке б/у фотоаппаратов через интернет, или для выбора лучшего фотоаппарата из нескольких, например, в магазине
   Для начала надо заполучить RAW'ки, сделанные в максимальном разрешении, в режиме приоритета выдержки, в режиме ручной фокусировки с закрытым объективом (крышечку с объектива не снимаем). Чтобы было какое-то единообразие, пусть ISO пусть будет 100. В интернете встречаются рекомендации по установке выдержки в 3-5 секунд... Я-перфекционист, хочу, чтобы выдержка была 10 секунд.
   Полученный "черный" кадр, конечно, можно и глазками посмотреть, но на дворе 21-й век! Поэтому немного схитрим.
   Открываем RAW-файл любом подходящем графическом редакторе. В моем случае любым подходящим оказался Lightroom. RAW-файл экспортируем в PSD в формате 16-битного sRGB.
   Теперь пару слов о том, что делает скрипт.
   В конце PSD файла в формате RRR GGG BBB находятся данные нашей картинки. Помним, что мы конвертировали наш файл в формате 16-битного sRGB. Обращаем внимание на то, что у нас структура файла не RGBRGBRGB... а RRRRRR...GGGGG...BBBBB. Я не обратил на это в первый раз внимания, за что и поплатился... :о)
   Скрипт читает заголовок файла, откуда получает информацию о размерах картинки и расположении блока Image Data Section, и начинает читать по два байта данные, пробегая массив данных RRRRR, затем GGGGG и наконец BBBBB, попутно "запоминая" элементы, яркость которых превышает указанный нами порог и выводит данные об обнаруженных точках в файл. Выводятся данные о координатах точек и яркости каналов, позволивших себе выделиться на общем фоне. :о).
   На моем ноуте первоначальный вариант скрипта разбирал 12 миллионов пикселей более чем за 340 секунд. Сейчас 17 миллионов пикселей скрипт разбирает чуть более чем за 90 чекунд. Но мне кажется, что и 90 секунд - не предел.
   Ну а теперь, собственно, сам скрипт.



//Каждый цвет может иметь интенсивность от 0 до 65535 и кодируется 2 байтами.
//Блок с данными находится в конце файла. 
//Перед ним блоки переменной длины, читаем заголовки.
//Ищем точки, в которых одна из компонент R,G или B превышает порог, 
//заданный в параметрах при запуске скрипта.
//Пороговое значение задается в процентах.
//
$reshandle=fopen($argv[1]."-".$argv[2].".res",'w');  //Открываем файл для резудбтатов
$start_time=time();     //Фиксируем время старта скрипта.
$header=array();
$handle=fopen($argv[1],'r');    //Открываем файл формата PSD, указанный в параметрах
$resarr=array();
$width=0;      //Длина картинки 
$height=0;      //Высота картинки
$pixcnt=0;
$limit=(int)((65535*$argv[2])/100);   //Рассчитываем пороговое значение
$cur_pos=0;      //Текущая позиция в файле
$content;      //А сюда будем читать байты (2 шт), отвечающие за один пиксель
$shine_num=0;      //Счетчик "битых" пикселей 

//В этом блоке читаем заголовок PSD файла и вытаскиваем оттуда размеры картинки
for ($i=0;$i<26;$i++)    
    {$byte = fread($handle,1);
     $header[$i]=$byte;
    }
$bh=array($header[14],$header[15],$header[16],$header[17]);
$bw=array($header[18],$header[19],$header[20],$header[21]);
$height=HEX_DEC_CONVERT($bh);
$width=HEX_DEC_CONVERT($bw);
$pixcnt=$height*$width;
//Выводим полученную информацию в файл
fwrite($reshandle,"Width (pxl): ".$width."\r\n");
fwrite($reshandle,"Height (pxl): ".$height."\r\n");
fwrite($reshandle,"Size (pxl): ".$pixcnt."\r\n=====================\r\n");


//Пойдем по-взрослому, с начала файла, читая заголовки блоков файла
$cur_pos=$cur_pos+26;
echo $cur_pos."\r\n";

//Color Mode Data Section
fseek($handle,$cur_pos);
for ($i=0;$i<4;$i++)    
    {$byte = fread($handle,1);
     $farr[$i]=$byte;
    }
$cur_pos=ftell($handle)+HEX_DEC_CONVERT($farr);
echo $cur_pos."\r\n";
fseek($handle,$cur_pos);

//Image Resources Section
fseek($handle,$cur_pos);
for ($i=0;$i<4;$i++)    
    {$byte = fread($handle,1);
     $farr[$i]=$byte;
    }
$cur_pos=ftell($handle)+HEX_DEC_CONVERT($farr);
echo $cur_pos."\r\n";
fseek($handle,$cur_pos);

//Layer and Mask Information Section
fseek($handle,$cur_pos);
for ($i=0;$i<4;$i++)    
    {$byte = fread($handle,1);
     $sarr[$i]=$byte;
    }
$cur_pos=ftell($handle)+HEX_DEC_CONVERT($sarr)+2;
echo $cur_pos."\r\n";
fseek($handle,$cur_pos);


$tmp_arr=array();
//====================
$i=0;
for($y=0;$y<$height;$y++)
   {for($x=0;$x<$width;$x++)     
      {
//читаем данные по одной точке - 2 байт. 
    $content=fread($handle,2);
    $tmp_arr[0]=$content[0];
    $tmp_arr[1]=$content[1];
    $R=HEX_DEC_CONVERT($tmp_arr);
//Сравниваем с пороговым значением. 
//Если есть необходимость - сохраняем в массиве
// координаты и значения компоненты RGB
    if ( ($R>$limit) )    
       {$resarr[$x][$y]['R']=$R;        
        $resarr[$x][$y]['G']=null;        
        $resarr[$x][$y]['B']=null;        
       }       
      }
   }

for($y=0;$y<$height;$y++)
   {for($x=0;$x<$width;$x++)     
      {
//читаем данные по одной точке - 2 байт. 
    $content=fread($handle,2);
    $tmp_arr[0]=$content[0];
    $tmp_arr[1]=$content[1];
    $G=HEX_DEC_CONVERT($tmp_arr);

    if ( ($G>$limit) )    
       {$resarr[$x][$y]['R']=null;        
        $resarr[$x][$y]['G']=$G;        
        $resarr[$x][$y]['B']=null;        
       }       
      }
   }

for($y=0;$y<$height;$y++)
   {for($x=0;$x<$width;$x++)     
      {
//читаем данные по одной точке - 2 байт. 
    $content=fread($handle,2);
    $tmp_arr[0]=$content[0];
    $tmp_arr[1]=$content[1];
    $B=HEX_DEC_CONVERT($tmp_arr);

    if ( ($B>$limit) )    
       {$resarr[$x][$y]['R']=null;        
        $resarr[$x][$y]['G']=null;        
        $resarr[$x][$y]['B']=$B;        
       }       
      }
   }
//Разбор массива и вывод результатов в файл
reset($resarr);
$R="";
$G="";
$B="";
while (list($key,$val)=each($resarr))
  {
   while (list($skey,$sval)=each($resarr[$key]))
     {if (!is_null($sval['R'])) {$R="  RED: ".$sval['R'];}
      if (!is_null($sval['G'])) {$G="  GREEN: ".$sval['G'];}
      if (!is_null($sval['B'])) {$B="  BLUE: ".$sval['B'];}
      
      fwrite($reshandle,"\r\nX: ".$key."; Y: ".$skey."    ".$R.$G.$B);
      $shine_num++;
     }


  }


//Цикл обработки закончился, выводим результаты работы
fwrite($reshandle,"\r\n================================\r\n");
fwrite($reshandle,"\r\nBrightness: ".$argv[2]."%");
fwrite($reshandle,"\r\nNumber of shine pixels :".$shine_num);
fwrite($reshandle,"\r\nWorking time (sec):".(time()-$start_time));

//echo ftell($handle)."\r\n";

fclose($handle);
fclose($reshandle);


//===================================================
//Переводим значения из двухбайтного HEX в DEC
function HEX_DEC_CONVERT(array $arg)
   {$result=0;
    $cnt=count($arg);
//echo $cnt."\r\n";
    for ($f=$cnt;$f>0;$f--)
       {$result=$result+ord($arg[$f-1])*pow(256,$cnt-$f);
   
       }
    return $result;
   }


   Чуть не забыл. Запускаем скрипт с параметрами. Первый параметр - имя файла. Второй параметр - процент.
php.exe shinepxl.php 5d.psd 5

На выходе получим файл с именем 5d.psd-5.res

Содержимое файла:

Width (pxl): 5184
Height (pxl): 3456
Size (pxl): 17915904
===============================================================

X: 24; Y: 2105      RED: 64646
===============================================================

Brightness: 40%
Number of shine pixels :1
Working time (sec):93

   В файле ширина, высота, общее количество точек. Потом перечисляются точки, у которых хотя бы одна составляющая R,G или B превышает заданное значение. Начало координатных осей - верхний левый угол.
   В конце - пороговое значение, число найденных точек и время обработки.
Как видим, в примере имеем один битый пиксель, светящийся красным.
   Вот, собственно, и все.


Комментариев нет:

Отправить комментарий