nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Тестовая картинка для видеопроцессора

Продолжаются раздумья, как этот видеопроцессор должен работать. Пора рассмотреть конкретный пример:



Так наша "мишень" должна выглядеть с расстояния 300 метров. Практически пустой кадр 1024х1024, а вся информативная часть умещается в 32х32 (именно такая эта картинка, я её увеличил до 64х64 методом nearest neighbour, просто чтобы лучше было видно).

Кажется, что именно работа с большой дальности для видеопроцессора наиболее "нервная" - счёт идёт на пиксели, и нужно успевать отправить измеренные результаты в QuatCore, а тем временем уже сбрасывать сумматоры и приниматься за следующий фрагмент!

Попытаемся понять, что же там должно твориться...


Режим сопровождения
наступает, когда мы уже знаем примерно, где расположены наши точки, и теперь хотим узнать их координаты с субпиксельной точностью.

Именно этот режим мы рассматриваем первым, поскольку тут мы заведомо должны поспевать за один кадр. В режиме захвата имеем право "втягиваться" в несколько заходов. Тоже, конечно, желательно за один такт управиться, и может даже на этом же такте сразу и с субпиксельной точностью яркостные центры найти, но это уже "пожелашки", и не самые важные.

На данном конкретном рисунке, самые яркие пиксели: (14;6), (21;6), (5;10) и (20;21). Исходя из экспозиции мы делаем совсем грубую оценку дальности (плюс-минус лапоть), которой, тем не менее, хватает, чтобы выбрать радиус пятен: 3 пикселя. В смысле что мы хотим использовать при подсчёте яркостного центра все точки (i;j), для которых (i-x0)2+(j-y0)2≤9.

До нулевой строки ни одно из пятен не простирается, поэтому мы должны "заказать" видеопроцессору поискать самый яркий пиксель на всей строке, т.е рассмотреть интервал [0;31]. Это нужно, чтобы убедиться: там, где мы не ожидаем увидеть ярких пятен, их действительно нет. Если там найдётся что-то яркое, и это что-то не будет вычитаться при помощи "темнового кадра", то нужно вернуться к режиму захвата, и скорее всего выдать ошибку. Я предпочитаю быть параноиками: лучше при малейшей неуверенности так и сказать, чем откидывать всё, что не укладывается в нашу "картину мира" и потом вмазаться на скорости...

На первой строке тоже пятен быть не должно, заказываем интервал [0;31]. То же самое на строке 2.

А вот на строку 3 уже могут "выскочить" пятна с центрами (14;6) и (21;6), ровно одним пикселем: (14;3) и (21;3). Поэтому строка разбивается на 5 частей:

[0;13] - найти самый яркий пиксель (и убедиться, что его яркость чрезвычайно мала),
{14} - найти яркостный центр (точнее, найти две суммы, которые QuatCore применит для нахождения яркостного центра),
[15;20] - найти самый яркий пиксель,
{21} - найти яркостный центр,
[22;31] - найти самый яркий пиксель.

Пожалуй, "найти самый яркий пиксель" я обзову "Захват", он же "захв", а "найти яркостный центр" обзову "Сопровождение", или "сопр".

На строке 4 у нас отрезки, относящиеся к пятнам, расширяются:

[0;11] - захв,
[12;16] - сопр,
[17;18] - захв,
[19;23] - сопр,
[24;31] - захв.

Ровно такая же картина на строке 5.

Строка 6 проходит ровно через центр пятен, поэтому тут они представлены наиболее широко:
[0;10] - захв,
[11;17] - сопр,
[18;24] - сопр,
[25;31] - захв.

Как видно, два пятна буквально упёрлись друг в друга!

И этого нам пока хватит для "раздумий".

Остальные пятна не так интересны - они соблюдают социальную дистанцию.

Захват
Ситуация, когда мы ничего не знаем, даже приближённо. Скажем, только начали работу, и обрабатываем первый кадр (или скорее первый кадр, где нас устраивает экспозиция, об этом тоже ещё предстоит подумать).

Вполне должна сработать такая метода:

заказываем целую строку, [0;31], на "захват" (т.е найти координаты самого яркого пикселя на строке и саму эту яркость).

Сначала это нулевая строка, там макс. яркость оказывается нулевой. У нас должен быть некий "порог обнаружения", посчитанный "по наихудшему случаю" (один светодиод отказал, их интенсивность - самая маленькая, какая только бывает согласно ТУ, уголковые отражатели максимально загрязнены, и находятся в самых неудачных углах, и на самом краю диаграммы направленности наших осветителей, и т.д., и потом поделить на 2, чтобы уж наверняка), и ноль явно будет меньше этого порога.

Та же история на первой, второй и третьей строках, там нули.

А вот на четвёртой строке самым ярким оказывается пиксель с координатами (14;4), его яркость 437, на шкале 0..4095 (12-битные отсчёты). QuatCore должен хранить список 4 самых ярких пикселей (скорее, чуть больше), вероятно в порядке убывания. Данная точка, (14;4) заносится в этот список, вместе с яркостью 437. И теперь, к следующей строке нужно сформировать участок вокруг неё, чтобы у нас не могло "плодиться" точек одна рядом с другой, а вместо этого обнаружение яркой точке в окрестности уже найденной лишь уточняло бы её координаты и яркость, т.е сначала схватились за край, а теперь подбираемся к центру!

Таким образом, на пятой строке теперь должно сформироваться 3 отрезка: [0;11] (поиск ДРУГОЙ точки), [12;16] (уточнение координат существующей) и [17;31] (поиск ДРУГОЙ точки).

На отрезке [0;11] не обнаруживается ничего (в смысле, макс. яркость 0. В реальности, из-за шумов матрицы, будет не ноль, но всё равно очень мало). На отрезке [12;16] обнаруживается макс. яркость 3040 (вот это уже ЗАЯВКА!) и коорд. (14;5). Понимаем, что точку (14;4) нужно заменить на (14;5), потому что здесь явно до центра ближе!

И наконец, на отрезке [17;31] обнаруживается макс. яркость 1687 (очень неплохо!) в точке с коорд. (21;5). Её мы тоже заносим в список, теперь ярких точек у нас ДВЕ. И готовим "заказы" видеопроцессору к следующей строке: теперь уже окрестности двух точек.

И так далее, и тому подобное. К концу кадра у нас образуется список из самых ярких точек. Их должно быть как минимум 4, иначе сразу "провал", а вот больше четырёх пусть будет. Когда начнётся "темновой кадр" (когда наши осветители отключены), мы закажем окрестности этих точек, и если там тоже найдём столь же яркие, то выкинем их как блики. И уже после этого выкидывания у нас должно остаться 4 точки. Если это так - всё хорошо, на следующем кадре можно уточнить их координаты уже в режиме сопровождения.

При работе с аналоговой камерой высокой четкости (то бишь, на лабораторном макете, сделанном "на коленке") ровно такой алгоритм вполне сработает, благодаря довольно длинному обратному ходу по строке. При работе с фотоприёмной матрицей 1205ХВ014 "обратный ход" составляет всего 8 тактов - маловато, чтобы QuatCore успел подготовиться к следующей строке, поэтому там, скорее всего, стоит разделить каждую строку на две половинки. Делаем первую половинку - забот не знаем, заказываем вторую - обрабатываем первую и придумываем интервалы для первой половины следующей строки. А уже на каком-нибудь "длинном перегоне" в первой половине следующей строки придумаем интервалы для второй половины. Как-то так...

Выводы
Работа видеопроцессора сводится к отработке отдельных горизонтальных отрезков. По сути, можно ввести команду наподобие

GPU    [X+i]

где данные будут отсылаться из шины данных в видеопроцессор. И данными этими будет длина очередного отрезка, тип работы (захват или сопровождение, то бишь нахождение максимума или нахождение двух сумм) и, возможно, "тип синхронизации". Нужно, чтобы даже допущенная однажды ошибка в подсчёте пикселей "сбросилась" к следующей строке, с приходом строчного синхроимпульса. Скажем, тип 0 будет означать "начинаем немедленно", тип 1: "начинаем с приходом строчного синхроимпульса", тип 2: "с приходом кадрового синхроимпульса". Всё вместе оно элементарно умещается в 16 бит, и даже "утрамбовывать" 3 числа в 16-битное значение (с помощью битовых сдвигов, побитовых OR и AND) не понадобится: просто будет где-нибудь в памяти хранится две константы, одна для начала новой строки и одна для начала нового кадра. Хотя для выбора типа работы можно и две отдельные команды ввести, скажем, вместо GPU это будут ACQ (от Acquire) и TRK (от Track).

"Застрять" процессор на передаче этих данных не должен. Потом, если отрезок длинный, процессор занимается своими делами - обработкой ранее полученных данных, и затем идёт команда наподобие
C      GPU


то есть это запрос на получение данных с видеопроцессора. Какие именно данные - определяется тем, что мы "заказали" ранее. Это либо максимальная яркость (12 бит) и X-координата самого яркого на этом отрезке пикселя (не более 10 бит). Либо: две суммы, притом довольно крупных, одна в 20 бит, вторая в 26 бит, т.е понадобится эдак 4 посылки по 16 бит, чтобы всё это передать.

И всё бы хорошо, если бы не ситуация "то густо, то пусто": то у нас "ничего не происходит" целую строку, 1024 такта подряд. И вдруг начинается серия из очень коротких посылок, вот как в начале поста:
[0;13] - захв
{14} - сопр
[15;20] - захв
{21} - сопр
[22;31] - захв

Только мы отправили "задание" для точки с коорд. 14, как уже должны снять данные, и тут же отправить новую посылку, а времени катастрофически нет, и шина перегружена.

Видимо, нужно ставить и на входе и на выходе FIFO, чтобы можно было и задание "вкачать" заблаговременно, и результаты можно было не торопясь принимать за 2..4 такта, без страха, что они "сгорят" из-за сброса сумматоров.

Хотя есть и другие варианты: мы могли бы дать видеопроцессору прямой доступ к памяти, пусть независимо от QuatCore туда записывает получившиеся результаты.

А может быть, я традиционно слишком жадный и проектирую слишком туповатый видеопроцессор, без своего царя в голове. А правильным решением было бы повысить его автономность: в пределе он сразу выдаст "состояние" (успех/провал) и координаты яркостных центров. Или что-то посередине.

Но тут не только думать, но и прыгать надо :) Вот пока написал программку, которая эту картинку перегоняет в .mif-файл (memory initialization file) для ПЛИС. Получилась такая простыня:
WIDTH=12;
DEPTH=1024;

ADDRESS_RADIX=UNS;
DATA_RADIX=HEX;

CONTENT BEGIN
	0	:	000
	1	:	000
	2	:	000
	3	:	000
	4	:	000
	5	:	000
	6	:	000
	7	:	000
	8	:	000
	9	:	000
	10	:	000
	11	:	000
	12	:	000
	13	:	000
	14	:	000
	15	:	000
	16	:	000
	17	:	000
	18	:	000
	19	:	000
	20	:	000
	21	:	000
	22	:	000
	23	:	000
	24	:	000
	25	:	000
	26	:	000
	27	:	000
	28	:	000
	29	:	000
	30	:	000
	31	:	000
	32	:	000
	33	:	000
	34	:	000
	35	:	000
	36	:	000
	37	:	000
	38	:	000
	39	:	000
	40	:	000
	41	:	000
	42	:	000
	43	:	000
	44	:	000
	45	:	000
	46	:	000
	47	:	000
	48	:	000
	49	:	000
	50	:	000
	51	:	000
	52	:	000
	53	:	000
	54	:	000
	55	:	000
	56	:	000
	57	:	000
	58	:	000
	59	:	000
	60	:	000
	61	:	000
	62	:	000
	63	:	000
	64	:	000
	65	:	000
	66	:	000
	67	:	000
	68	:	000
	69	:	000
	70	:	000
	71	:	000
	72	:	000
	73	:	000
	74	:	000
	75	:	000
	76	:	000
	77	:	000
	78	:	000
	79	:	000
	80	:	000
	81	:	000
	82	:	000
	83	:	000
	84	:	000
	85	:	000
	86	:	000
	87	:	000
	88	:	000
	89	:	000
	90	:	000
	91	:	000
	92	:	000
	93	:	000
	94	:	000
	95	:	000
	96	:	000
	97	:	000
	98	:	000
	99	:	000
	100	:	000
	101	:	000
	102	:	000
	103	:	000
	104	:	000
	105	:	000
	106	:	000
	107	:	000
	108	:	000
	109	:	000
	110	:	000
	111	:	000
	112	:	000
	113	:	000
	114	:	000
	115	:	000
	116	:	000
	117	:	000
	118	:	000
	119	:	000
	120	:	000
	121	:	000
	122	:	000
	123	:	000
	124	:	000
	125	:	000
	126	:	000
	127	:	000
	128	:	000
	129	:	000
	130	:	000
	131	:	000
	132	:	000
	133	:	000
	134	:	000
	135	:	000
	136	:	000
	137	:	000
	138	:	000
	139	:	000
	140	:	000
	141	:	11D
	142	:	1B5
	143	:	1B5
	144	:	072
	145	:	000
	146	:	000
	147	:	000
	148	:	000
	149	:	0AA
	150	:	013
	151	:	000
	152	:	000
..... 
END;

(там 1024 строки)

Но зато, если получится по-задуманному, оно влезет в 5576ХС6Т (2880 ЛЭ и 5 килобайт памяти) СО ВСЕМИ НАВОРОТАМИ :) Надо же свою леворукость как-то оправдывать...


Poll #2102244 Данные с видеообработчика

Как передавать данные на процессор QuatCore?

Обычным способом, через шину данных, но ввести буфер на 2-3 отрезка
2(66.7%)
Прямой доступ к памяти (DMA)
1(33.3%)
Видеообработчик должен "поумнеть" и не дёргать QuatCore по пустякам
0(0.0%)
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Нахождение двух самых отдалённых точек

    Пока компьютер долго и упорно мучал симуляцию, я пытался написать на ассемблере алгоритм захвата на ближней дистанции. А сейчас на этом коде можно…

  • Слишком общительный счётчик

    Вчера я чуть поторопился отсинтезировать проект,параметры не поменял: RomWidth = 8 вместо 7, RamWidth = 9 вместо 8, и ещё EnableByteAccess=1, чтобы…

  • Балансируем конвейер QuatCore

    В пятницу у нас всё замечательно сработало на симуляции, первые 16 миллисекунд полёт нормальный. А вот прошить весь проект на ПЛИС и попробовать "в…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 3 comments