Я ищу самый быстрый способ вычисления вектора направления на основе произвольного цвета в изображении (камера Rpi, но файл JPEG для тестирования в настоящее время в порядке), то есть отслеживание проекта с цветным шариком. Обратите внимание, что полученный вектор (или координаты центроида, что угодно) должен быть передан в PHP для выполнения программы , поэтому решение, которое я ищу, должно заканчиваться PHP, но до этого может быть что угодно,учитывая, что это может быть реализовано как в Windows, так и в Linux.
Рассмотрим входное изображение JPEG:
Вот 2 примера вектора направленияЯ после, получен на основе 1) ввода цвета чирка и 2) ввода фиолетового цвета. Очевидно, что за один раз будет задан только 1 вектор, я положил 2, чтобы продемонстрировать несколько примеров на 1 изображении, но это всегда будет только 1 вектор за раз. Обратите внимание, что результирующие векторы («v») стандартизированы от -1,0 (внизу / слева) до +1,0 (внизу / справа), так что ноль - это середина изображения.
Вот различные решения, которые я реализовал / протестировал до сих пор, и сколько времени занимает весь процесс, на основе изображения JPEG 960x640, но реализованное решение будет привязано к входу камеры Rpi, У меня еще нет камеры, поэтому я использую изображение JPEG, пока камера не прибудет из Китая.
1) 2700ms : использовать 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) 700ms : То же, что # 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% -ный параметр fuzz и модификатор 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) 32ms : PHP создает текст «in»файл, содержащий путь к изображению и цветовой ключ / цвет, и начинает цикл до тех пор, пока не будет прочитан текстовый файл «out». Сценарий Python + OpenCV уже / всегда выполняет (останавливаемый) бесконечный цикл, постоянно ищущий текстовый файл «in» и, когда он существует, он читает его, анализирует значения, создает 1-битную маску, используя значения HSV ~ 10%(cv2.inRange) из файла «in», затем создает массив с использованием cv2.findNonZero (mask), вычисляет среднее значение массива и записывает его в текстовый файл «out», который 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 МГц, поэтому он должен быть быстрым. Я думаю, что 30 мс на процессоре с частотой 900 МГц будет около 150-200 мс (еще не проверено, ожидая отправки камеры)