Я ищу самый быстрый способ вычислить вектор направления на основе произвольного цвета изображения (камера Rpi, но файл JPEG для тестирования пока в порядке), также известный как отслеживание проекта цветного шара. Обратите внимание, что результирующий вектор (или координаты центроида, что угодно) необходимо передать в PHP для выполнения программы, поэтому решение, которое я ищу, должно заканчиваться на PHP, но может быть чем угодно до , учитывая, что его можно реализовать как в Windows, так и в Linux.
Рассмотрим входное изображение JPEG:
Вот 2 примера векторов направления, которые мне нужны, полученные на основе 1) ввода бирюзового цвета и 2) ввода фиолетового цвета. Очевидно, что одновременно будет запрашиваться только 1 вектор, я поставил 2, чтобы продемонстрировать несколько примеров в 1 изображении, но это всегда будет только 1 вектор за раз. Обратите внимание, что результирующие векторы («v») стандартизированы от -1,0 (внизу/слева) до +1,0 (внизу/справа), так что ноль находится в середине изображения.
Вот различные решения, которые я реализовал / протестировал до сих пор, и сколько времени занимает весь процесс, основанный на изображении JPEG 960x640, но реализованное решение будет привязано к входу камеры Rpi, у меня пока нет камеры, поэтому Я использую изображение в формате JPEG, пока камера не прибудет из Китая.
1) 2700 мс: используйте GD2, который связан с PHP, для цикла по каждому пикселю, нажмите пиксели, соответствующие ~ 10% значений RGB в массивах XY, усредните массивы XY, вычислите/нормализуйте вектор направления из массивов XY .
$arr_matching_pixels = array('arr_x' => array(), 'arr_y' => array());
for($y = 0; $y < $h - 1; $y++){
for($x = 0; $x < $w - 1; $x++){
$arr_pixel = imagecolorsforindex($img, imagecolorat($img, $x, $y));
if(abs($arr_pixel['red'] - $arr_seek_color['red']) < 30){
if(abs($arr_pixel['green'] - $arr_seek_color['green']) < 30){
if(abs($arr_pixel['blue'] - $arr_seek_color['blue']) < 30){
array_push($arr_matching_pixels['arr_x'], $x);
array_push($arr_matching_pixels['arr_y'], $y);
}
}
}
}
}
// Compute centroid of color... etc...
2) 700 мс: то же, что и № 1, за исключением того, что начните с изменения размера холста на 50% (приемлемая потеря) с помощью imagecreatefromjpeg('_test_cam_img.jpg');
3) 560 мс: то же, что и № 2, за исключением использования ImageMagick с циклом итератора пикселей для чтения пикселей.
$imagick = new Imagick(realpath($o_img));
$arr_matching_pixels = array('arr_x' => array(), 'arr_y' => array());
$arr_pixel = array();
$iterator = $imagick->getPixelIterator();
foreach($iterator as $y => $pixels){
foreach($pixels as $x => $pixel){
$arr_pixel = $pixel->getColor();
if(abs($arr_pixel['r'] - $arr_seek_color['red']) < 30){
if(abs($arr_pixel['g'] - $arr_seek_color['green']) < 30){
if(abs($arr_pixel['b'] - $arr_seek_color['blue']) < 30){
array_push($arr_matching_pixels['arr_x'], $x);
array_push($arr_matching_pixels['arr_y'], $y);
}
}
}
}
}
// Compute centroid of color... etc...
4) 340 мс: вызовите системный двоичный файл ImageMagick с помощью функции exec(), передайте ему местоположение изображения, ключ цветности/цвета, параметр изменения размера на 50%, параметр размытия на 10% и параметр sparse-color: модификатор для извлечения текстового (CSV-подобного) списка желаемых пикселей, затем используйте PHP для циклического перебора каждой строки, разделения запятых и перемещения всех пикселей в массивах XY, усреднения массивов XY, вычисления/нормализации вектора направления из массивы XY. Я заметил, что вызов exec() оказывается гораздо медленнее, чем выполнение той же команды непосредственно из командной строки Windows.
$imagick = new Imagick(realpath($o_img));
$out = exec('"E:\Users\Ben\Roaming Apps\imagemagick-6.9.3\convert" E:\wamp64\www\test_cam_img.jpg -resize 50% -fuzz 10% +transparent rgb(' . $arr_seek_color['red'] . ',' . $arr_seek_color['green'] . ',' . $arr_seek_color['blue'] . ') sparse-color:');
$arr_lines = explode(' ', $out);
$arr_matching_pixels = array('arr_x' => array(), 'arr_y' => array());
foreach($arr_lines as $str_line){
$arr_xy_coords = explode(',', $str_line);
array_push($arr_matching_pixels['arr_x'], $arr_xy_coords[0]);
array_push($arr_matching_pixels['arr_y'], $arr_xy_coords[1]);
}
// Compute centroid of color... etc...
5) 32 мс: PHP создает "входящий" текстовый файл, содержащий путь к изображению и ключ цветности/цвета, и начинает цикл до тех пор, пока не прочитает "исходящий" текстовый файл. Сценарий python + OpenCV уже/всегда запускает (останавливаемый) бесконечный цикл, постоянно ищущий «внутри» текстовый файл, и когда он существует, он читает его, взрывает значения, создает 1-битную маску, используя значения HSV ~ 10% (cv2.inRange) из «входящего» файла, затем создает массив с помощью cv2.findNonZero(mask), вычисляет среднее значение массива и записывает его в «исходящий» текстовый файл, который PHP немедленно считывает, содержащий значение вектора направления. Это, безусловно, самый быстрый способ, который я нашел, но он неудобен, потому что подразумевает, что скрипт python должен быть запрограммирован в CRONJOB и отслеживаться/перезапускаться в одном экземпляре в случае сбоя.
file_put_contents('_avg_color_coords_in.txt', $o_img . "\n" . $arr_seek_color['h'] . ',' . $arr_seek_color['s'] . ',' . $arr_seek_color['l']);
$starttime = time();
while((time() - $starttime) < 5){ // Max 5 seconds (exaggerated)
if(file_exists('_avg_color_coords_out.txt')){
$dir_vector = (float) file_get_contents('_avg_color_coords_out.txt');
if(!@unlink('_avg_color_coords_out.txt')){
sleep(1);
unlink('_avg_color_coords_out.txt');
}
break;
}
usleep(2000);
}
// $dir_vector ("v", the centroid of the color) is already computed by Python
// ---------- PYTHON SCRIPT ----------
import math
import cv2
import numpy as np
import os
import time
#cap = cv2.VideoCapture(0)
#while (1):
# _, frame = cap.read()
if(os.path.exists('_avg_color_coords_stop.txt')):
exit()
while not os.path.exists('_avg_color_coords_in.txt'):
time.sleep(0.002)
f = open('_avg_color_coords_in.txt', 'r')
imgsrc = f.readline().rstrip('\n')
rgbcol = [int(x) for x in f.readline().rstrip('\n').split(',')]
frame = cv2.imread(imgsrc)
h, w = frame.shape[:2]
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hfacl = rgbcol[0] / 360 * 180 * 0.95
hfach = rgbcol[0] / 360 * 180 * 1.05
sfacl = rgbcol[1] / 100 * 255 * 0.9
sfach = rgbcol[1] / 100 * 255 * 1.1
vfacl = rgbcol[2] / 100 * 255 * 0.9
vfach = rgbcol[2] / 100 * 255 * 1.1
lower_color = np.array([hfacl, sfacl, vfacl]) # 0..180, 0..255, 0..255 not percentage!
upper_color = np.array([hfach, sfach, vfach]) # 0..180, 0..255, 0..255 not percentage!
mask = cv2.inRange(hsv, lower_color, upper_color)
#cv2.imshow('mask', mask)
points = cv2.findNonZero(mask)
if(points.any()):
avg = np.mean(points, axis=0)
else:
avg = [0,0]
#print(avg)
v = -math.atan(((w * 0.5) - avg[0][0]) / (h - avg[0][1])) / (3.1415 * 0.5);
f2 = open('_avg_color_coords_out.txt', 'w+')
f2.write("%s" % str(v))
# k = cv2.waitKey(5) & 0xff
# if k == 27:
# break
#cv2.destroyAllWindows()
#cap.release()
f2.close()
f.close()
os.remove('_avg_color_coords_in.txt')
6) 38 мс: то же, что и № 5, за исключением того, что начните с изменения размера холста на 50% (приемлемая потеря), что, похоже, вообще не ускоряет работу и даже кажется немного контрпродуктивным.
Есть ли более быстрый способ или этот оптимален? Это будет работать каждую секунду на 900 МГц Rpi, поэтому оно должно быть быстрым. Я думаю, что 30 мс на процессоре 900 МГц будет около 150-200 мс (еще не тестировалось, жду отправки камеры)