nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Возвращаемся к алгоритму обнаружения

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

Узнал, но не очень понял, "что за этим стоит" - вроде бы алгоритм простенький, но очень восприимчив к "граничным условиям" - чуть-чуть не так сделал - и пятна не будут "расти в размере" с должной скоростью, "поглощая" обнаруженные вокруг них яркие пиксели, или будет плодиться целый выводок мелких пятен вместо крупных, или пятна однажды пересекутся, а объединиться уже не смогут, поскольку у меня объединение происходит, когда между существующими пятнами обнаружен яркий пиксель, но если между ними не остаётся ни одного пикселя - искать ничего не будут.

Сейчас категорически не хочется выводить "теорию функционирования", из которой станет ясно, при каких условиях будет "всё хорошо". Как-нибудь потом. А пока просто внесу те изменения в ассемблерный код и в верилог, после которых оно должно заработать, по крайней мере на тех 3 масштабах: 0,5 метра, 1 метр и 2 метра, с которыми успел поиграться.

И начнём с модернизации "видеопроцессора", чтобы он не "проглатывал" пиксели на границах отрезков!


Казалось бы, 1 пиксель - чего такого? Но если в модели сделать такое поведение - ничего хорошего не жди:


Здесь если послать ACQ X, будет найден самый яркий пиксель на отрезке до X, НЕ ВКЛЮЧАЯ сам X. На следующий отрезок начнётся с X+1, и непосредственно пиксель с номером X будет похерен.

А вот если следующий отрезок начинать с X, то есть не пропускать ни одного пикселя, получается всё как надо:


Давайте подрихтуем наш видеопроцессор, благо, это не должно его усложнить. Нас интересует модуль PixelSumMax, забавная штука, которая может либо найти сумму яркостей всех пикселей на отрезке, либо найти максимальную яркость и X-координату, на которой она достигается. Вот изначальная идея, но потом пришлось его доработать напильником.

Приведём код, благо он небольшой:
//"комбинированный модуль":
//в режиме захвата он находит максимум и запоминает его координату,
//а в режиме сопровождения находит сумму

//в принципе, максимум и его координата занимают столько же места,
//как и сумма "без потерь"

//isSum=1: суммируем,
//isSum=0: находим максимум
module PixelSumMax (input clk, input sclr, input isSum, input [PixelWidth-1:0] Pixel, input [XregWidth-1:0] X, output [PixSumWidth-1:0] Q);

parameter PixelWidth = 12;	//8 для китайской камеры, если не хотим делать гамма-коррекцию, 12 во всех остальных случаях
parameter PixSumWidth = 20;	//20 хватит в случае 12 бит, 16 хватит в случае 8 бит. Когда сообразим реальные размеры точек на малых дистанциях, можем ещё чуть сократить
parameter XregWidth = 8;

localparam CounterWidth = PixSumWidth - PixelWidth;

	wire [PixelWidth-1:0] Sum;
	reg [PixelWidth-1:0] LowerQ = 1'h0;
	wire PlusOne;
	lpm_add_sub Adder (	.dataa (LowerQ),
				.datab (Pixel),
				.cin (1'b0),
				.result (Sum),
				.cout (PlusOne));
	defparam
		Adder.lpm_direction = "ADD",
		Adder.lpm_hint = "ONE_INPUT_IS_CONSTANT=NO,CIN_USED=NO",
		Adder.lpm_representation = "UNSIGNED",
		Adder.lpm_type = "LPM_ADD_SUB",
		Adder.lpm_width = PixelWidth;	
		
	always @(posedge clk) if (isSum | PlusOne | sclr)
		LowerQ <= sclr? {PixelWidth{~isSum}} : isSum? Sum : ~Pixel;
		
	wire [CounterWidth-1:0] CounterOut;
	lpm_counter UpperBits (	.clock (clk),
				.cnt_en (PlusOne&isSum),
				.sclr (sclr),
				.data (X),
				.sload (PlusOne&(~isSum)),
				.Q (CounterOut));
	defparam
		UpperBits.lpm_direction = "DOWN",
		UpperBits.lpm_port_updown = "PORT_UNUSED",
		UpperBits.lpm_type = "LPM_COUNTER",
		UpperBits.lpm_width = CounterWidth;
		
	assign Q = {CounterOut, LowerQ};
endmodule


Самое главное - как здесь производится обнуление по приходу sclr. Регистр LowerQ, который представляет младшие биты (по разрядности АЦП), устанавливается во все единицы при нахождении максимума, либо во все нули при нахождении суммы. В это же самое время, счётчик UpperBits сбрасывается в ноль.

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

Приведём исправленный код:
//"комбинированный модуль":
//в режиме захвата он находит максимум и запоминает его координату,
//а в режиме сопровождения находит сумму

//в принципе, максимум и его координата занимают столько же места,
//как и сумма "без потерь"

//isSum=1: суммируем,
//isSum=0: находим максимум
module PixelSumMax (input clk, input sclr, input isSum, input [PixelWidth-1:0] Pixel, input [XregWidth-1:0] X, output [PixSumWidth-1:0] Q);

parameter PixelWidth = 12;	//8 для китайской камеры, если не хотим делать гамма-коррекцию, 12 во всех остальных случаях
parameter PixSumWidth = 20;	//20 хватит в случае 12 бит, 16 хватит в случае 8 бит. Когда сообразим реальные размеры точек на малых дистанциях, можем ещё чуть сократить
parameter XregWidth = 8;

localparam CounterWidth = PixSumWidth - PixelWidth;

	wire [PixelWidth-1:0] Sum;
	reg [PixelWidth-1:0] LowerQ = 1'h0;
	wire PlusOne;
	lpm_add_sub Adder (	.dataa (LowerQ),
				.datab (Pixel),
				.cin (1'b0),
				.result (Sum),
				.cout (PlusOne));
	defparam
		Adder.lpm_direction = "ADD",
		Adder.lpm_hint = "ONE_INPUT_IS_CONSTANT=NO,CIN_USED=NO",
		Adder.lpm_representation = "UNSIGNED",
		Adder.lpm_type = "LPM_ADD_SUB",
		Adder.lpm_width = PixelWidth;	
		
	always @(posedge clk) if (isSum | PlusOne | sclr)
		LowerQ <= isSum? (sclr? {PixelWidth{1'b0}} : Sum) : ~Pixel;
		
	wire [CounterWidth-1:0] CounterOut;
	lpm_counter UpperBits (	.clock (clk),
				.cnt_en (PlusOne&isSum),
				.sclr (sclr & isSum),
				.data (X),
				.sload ((PlusOne | sclr)&(~isSum)),
				.Q (CounterOut));
	defparam
		UpperBits.lpm_direction = "DOWN",
		UpperBits.lpm_port_updown = "PORT_UNUSED",
		UpperBits.lpm_type = "LPM_COUNTER",
		UpperBits.lpm_width = CounterWidth;
		
	assign Q = {CounterOut, LowerQ};
endmodule


Когда isSum=1, ничего не изменяется - в LowerQ загружаются нули, равно как и в UpperBits, чтобы суммирование начиналось с нуля. В счётчике UpperBits никогда не срабатывает "параллельная загрузка" - он исключительно прибавляет по единичке, когда приходит перенос с младших разрядов.

А вот когда isSum=0, поведение немного меняется. Вместо обнуления UpperBits происходит всё та же параллельная загрузка, равно как и вместо обнуления LowerQ (точнее установки "всех единиц") - загружается текущий пиксель, инвертированный.

В некотором роде, код даже упростился! А синтезируется этот модуль в те же самые 49 ЛЭ, что и раньше, это при 12-битном АЦП и 8 битах для представления X-координаты.

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


Не забываем "особенность" этого модуля, что в режиме максимума он в старших битах выдаёт X-координату (при наших настройках это первые 2 hex-цифры), а в младших - максимальную яркость, но С ИНВЕРСИЕЙ.

Поэтому первый раз у нас поступает координата X=0 и инверсия от нулевого пикселя, FFF. Затем координата обновляется до 01, и выдаётся новая яркость, инверсия от 0x753 = 0x8AC. Затем, когда поступает ещё бОльшая яркость 0x9BE, происходит следующее обновление - координата 02 и инверсия 0x641. (чтобы "в уме" делать инверсию, надо из 1510, т.е 0xF, вычитать каждую циферку. Было 0xF - станет 0, было 0xE - станет 1, и так далее). Затем приходит более тусклый пиксель 891 - и обновления не происходит, всё верно.

Внимательно наблюдаем за сбросом при X=5. В этот самый момент можно забирать результаты по первому отрезку (так мы всегда и делали). И уже к следующему такту мы имеем X=5 (т.е защёлкнуты координаты первой точки НОВОГО отрезка) и яркость 0x430, то есть инверсия от 0xBCF.

И как оказывается, это самый яркий пиксель, мы видим, что каждый следующий не может "обновить" нам значения, поэтому мы так и доводим X=5, Lum=0x430 до следующего сброса. И там мы сделали то же самое с самым ярким пикселем 0xFCA.

По части максимума всё верно. А вот при суммировании происходит что-то нехорошее!

В старших 8 битах поначалу были нули - так и положено, но когда было просуммировано сколько-то пикселей, там появилось FF. СЧЁТЧИК РАБОТАЕТ НЕ В ТУ СТОРОНУ! Он должен по единичке ПРИБАВЛЯТЬ, когда есть перенос с младших разрядов, а он вычитает.

Самое смешное, это не ВНЕСЁННАЯ ошибка, она была здесь раньше. И увы, при тестировании второго сумматора мы её не отследили!

Исправление элементарное, надо lpm_direction поменять с "DOWN" на "UP":



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

Ладно, теперь подправить ассемблерный код в паре мест - и можно пробовать дальше.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

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

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

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

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

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

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

  • Ковыряемся с сантехникой

    Наконец-то закрыл сколько-нибудь пристойно трубы, подводящие к смесителю, в квартире в Москве: А в воскресенье побывал на даче, там очередная…

  • Мартовское велосипедное

    Продолжаю кататься на работу и с работы на велосипеде, а также в РКК Энергию и на дачу. Хотя на две недели случился перерыв, очередная поломка,…

  • Обнаружение на новом GPU - первые 16 мс

    Закончилась симуляция. UFLO и OFLO ни разу не возникли, что не может не радовать. За это время мы дошли до строки 0x10F = 271. Поглядим дамп памяти:…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments