nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Дорабатываем программу обнаружения

Итак, видеопроцессор мы подрихтовали, даже сохранив такое же число логических элементов (ЛЭ). Теперь нужно подправить программу в нескольких местах.


1. Условие на поглощение пикселя справа от пятна.
Было вот так:

	;первым делом, проверяем, не сливается ли с пятном слева
	Acc 		[SP+1]
	SUB		[X+2j+k]
	DIV2S		D1
	DIV2S		[X+1]
	Z		[X+k]		;указатель на пятно "справа"
	JL		@@LeftMerge	;сейчас процедура слияния заметно усложнилась, приходится 2 отдельные ветви


Из X-координаты только что обнаруженного яркого пикселя вычиталась X-координата центра пятна, затем половина его диаметра и половина "минимального" диаметра D1, и если ответ получался строго отрицательным - делается поглощение, либо слияние двух пятен.

Как показало моделирование, такое условие слишком строгое - зачастую даже для ближайшей точки правее пятна оно не может выполниться. Нужно строгое неравенство "<0" заменить нестрогим "≤0", но такого в нашем процессоре нет. Зато есть "≥0", а чтобы им воспользоваться, нужно всем операндам поменять знак, благо этот результат мы не используем в дальнейшем, он нужен только для принятия решения. Получается так:
	;первым делом, проверяем, не сливается ли с пятном слева			
	Acc	[X+2j+k]
	SUB	[SP+1]
	DIV2A	D1
	DIV2A	[X+1]
	Z	[X+k]
	JGE	@@LeftMerge


Компилируется, никаких Hazard'ов не возникает, вроде всё нормально...

2. Условие слияния двух пятен.

Идёт сразу после прыжка в @@LeftMerge:
		;обнаруженная точка примыкает к пятну слева от себя. Возможно, потребуется слияние и с пятном справа от себя
		;X указывает на пятно слева, Z - на пятно справа 
@@LeftMerge:	Acc	[Z+2j+k]	;взяли X-координату правого пятна
		SUB	[X+2j+k]	;вычитаем X-координату левого
		DIV2S	[Z+1]		;вычитаем радиус правого пятна
		DIV2S	[X+1]		;и радиус левого пятна
		SUB	D1		;и минимально допустимый диаметр пятна
		JL	@@MergeBlobs	;делаем слияние ПЯТЕН


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

Та же проблема: условие оказалось слишком жёстким. Когда между пятнами ещё большой зазор, оно не срабатывает, вместо этого одно из пятен растёт, поглощая только что обнаруженный яркий пиксель между ними. Тем временем и пятно справа прирастает, обнаружив яркий пиксель справа от себя, и может так выйти, что пятна уже пересеклись, и отрезка между ними больше нет.

Условие нужно ослабить, но просто замена "<" на "≤" оказывается недостаточной. А вот если D1 увеличить на единичку - уже сработает. Просто введём константу D1p1 (D1 прибавить один) - жаль, что мой компилятор не умеет обрабатывать выражения в операндах, наподобие D1+1, может как-нибудь введём такую функциональность, а пока лень... Ну и доработанный вариант будет таков:

		;обнаруженная точка примыкает к пятну слева от себя. Возможно, потребуется слияние и с пятном справа от себя
		;X указывает на пятно слева, Z - на пятно справа 
@@LeftMerge:	Acc	[Z+2j+k]	;взяли X-координату правого пятна
		SUB	[X+2j+k]	;вычитаем X-координату левого
		DIV2S	[Z+1]		;вычитаем радиус правого пятна
		DIV2S	[X+1]		;и радиус левого пятна
		SUB	D1p1		;и минимально допустимый диаметр пятна
		JL	@@MergeBlobs	;делаем слияние ПЯТЕН


Количество строк кода не поменялось, новые Hazard'ы не всплыли, компилится нормально. Уже радость...

3. Обновление параметров пятна при поглощении точки справа от себя
Сейчас это делается так:

;если дошли до этого места, значит пятна сливать не нужно, достаточно лишь уточнить параметры левого пятна
;увы, аккумулятор совсем другим набит, нужно "с нуля" начинать
	Acc		[SP+1]	;Acc = x, т.е координата обнаруженной точки
	SUB		[X+2j+k]	;вычитаем X, т.е Acc = x - X - разность между обнаруженной точкой и центром левого пятна
	DIV2A		[X+1]		;прибавили D/2, получаем Acc = x-X+D/2 
	[X+1]		Acc		;обновили диаметр левого пятна.
	Acc		1
	;теперь подкорректировать X-координату
	ADD		[SP+1]	;берём координату обнаруженной точки
	DIV2S		[X+1]		;вычитаем половинку диаметра
	[X+2j+k]	Acc
	JMP		AcqNoCheck	;если слева от нас есть РЕАЛЬНОЕ пятно (фиктивное мы трогать не можем, его X-координата "-32768", условие заведомо не выполнится),
	;значит проверять "есть ли за нами должок" не нужно - точно есть!						


Причём, здесь уже внесено изменение, которое я ещё не успел проверить. Когда обнаружилось, что пятно упорно не хочет смещаться вправо во время "поглощения" точки, я добавил сюда единичку, и это правильно. Но этого недостаточно, единичку надо прибавить и к новому диаметру.

Более простой вариант, где просто прибавляется по единичке и к диаметру, и к координате центра, здесь "не прокатывает", у меня две мишени сливаются в одно большое пятно, нехорошо это. Что интересно, по Y-координате такой "упрощённый" вариант проходит - не удивлюсь, если наш алгоритм оказался заточен как раз под эллипсы, хоть я этого и не хотел особо. Но со всеми этими строгими/нестрогими неравенствами, правилами округления ковырялся до тех пор, пока не заработало именно на них... Из-за чего и выползла "анизотропия".

Ладно, ещё одна единичка - и должно заработать:
;если дошли до этого места, значит пятна сливать не нужно, достаточно лишь уточнить параметры левого пятна
;увы, аккумулятор совсем другим набит, нужно "с нуля" начинать
	Acc		[SP+1]	;Acc = x, т.е координата обнаруженной точки
	SUB		[X+2j+k]	;вычитаем X, т.е Acc = x - X - разность между обнаруженной точкой и центром левого пятна
	DIV2A		[X+1]		;прибавили D/2, получаем Acc = x-X+D/2 
	ADD		1
	[X+1]		Acc		;обновили диаметр левого пятна.
	Acc		1
	;теперь подкорректировать X-координату
	ADD		[SP+1]	;берём координату обнаруженной точки
	DIV2S		[X+1]		;вычитаем половинку диаметра
	[X+2j+k]	Acc
	JMP		AcqNoCheck	;если слева от нас есть РЕАЛЬНОЕ пятно (фиктивное мы трогать не можем, его X-координата "-32768", условие заведомо не выполнится),
	;значит проверять "есть ли за нами должок" не нужно - точно есть!						


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

Компилируется нормально, новых Hazard'ов не появляется, хорошо.

4. Условие поглощения точки пятном справа от себя
Было так:

	;если дошли досюда, значит с пятном слева не сливаемся, надо проверить теперь на пятно справа						
	Acc	[Z+2j+k]
	SUB	[SP+1]
	DIV2S	D1
	DIV2S	[Z+1]
	JL	@@RightMerge


В очередной раз условие слишком строгое, нужно его немного смягчить. Эх, не понимаю до конца, как это всё работает. Вижу на модели, что чуть-чуть эти условия поменяй - и начинают лишние точки плодиться, а почему - кто их знает. Примерно понимаю: если одно пятно "отпочковывается" слишком близко к другому - они изначально начнут пересекаться, и объединиться им уже не судьба. Может и стоит критерий на слияние всегда проверять, на каждой итерации, а не только когда между ними "мостик" образовывается. Но вот тогда разграничить пятна, между которыми зазор всего в пару пикселей, точно станет проблематично. В общем, я пока знаю один рецепт, буду следовать ему, но наверняка он не единственный, и если посерьёзнее подумать, можно будет упростить. Давайте это будет наш "запас", если вдруг место начнёт заканчиваться...

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

	;если дошли досюда, значит с пятном слева не сливаемся, надо проверить теперь на пятно справа						
	Acc	[Z+2j+k]
	SUB	[SP+1]
	DIV2S	D1p1
	DIV2S	[Z+1]
	JL	@@RightMerge


5. Обновление параметров пятна при поглощении точки слева от него

Здесь был такой код:
@@RightMerge:	ADD		[Z+1]	;в аккумуляторе лежало x-X-D/2-D1/2. Теперь прибавили D, получили x-X+D/2-D1/2
		DIV2A		D1	;прибавили D1/2, получили x-X+D/2 - новый диаметр пятна
		[Z+1]		Acc	;ага, записали.
		;теперь подкорректировать X-координату
		DIV2		Acc
		ADD		[SP+1]
		[Z+2j+k]	Acc
;а теперь с чистой совестью отправляем задание на обработку точки
		JMP		TaskPending



Тут придётся ввести ЕЩЁ ОДНУ константу, D1p2 (то есть на этот раз "плюс два"), чтобы скомпенсировать излишне отрицательный результат после сравнения, и ещё чуть-чуть добавить "от себя" (чтобы пятно могло расти с должной скоростью). А потом ещё одну строку кода, "SUB 1" для нахождения новой X-координаты, иначе пятно не желает сдвигаться влево в процессе поглощения:

@@RightMerge:	ADD		[Z+1]	;в аккумуляторе лежало x-X-D/2-D1/2. Теперь прибавили D, получили x-X+D/2-D1/2
		DIV2A		D1p2	;прибавили D1/2, получили x-X+D/2 - новый диаметр пятна
		[Z+1]		Acc	;ага, записали.
		;теперь подкорректировать X-координату
		DIV2		Acc
		ADD		[SP+1]
		SUB		1
		[Z+2j+k]	Acc
;а теперь с чистой совестью отправляем задание на обработку точки
		JMP		TaskPending


Всё, теперь строк кода стало 257, но ещё подсокращу.

6. Выдача заданий на обработку для следующей строки изображения

Сейчас:
AcqNoCheck:	Acc	[X+2j+k]
		DIV2S	[X+1]	;теперь в аккмуляторе у нас X - Ceil(D/2)
		ACQ	Acc	;первый отрезок
		ADD	[X+1]	;а вот теперь X + Floor(D/2)
		ACQ	Acc	;второй отрезок


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

В итоге добавляем ещё одну строчку:
AcqNoCheck:	Acc	[X+2j+k]
		DIV2S	[X+1]	;теперь в аккмуляторе у нас X - Ceil(D/2)
		DIV2A	1	;чтобы всё-таки было X-Floor(D/2)
		ACQ	Acc		;первый отрезок
		ADD	[X+1]	;а вот теперь X + Floor(D/2)
		ACQ	Acc		;второй отрезок


Эх, неправильно это, но очень уж хочется посмотреть это хозяйство в работе.

258 слов кода...

7. Проверка по вертикали

Было так:
;входит ли этот отрезок в уже найденное пятно?
Acc	[SP+2j+k]		;номер текущей строки изображения
DIV2S	[X+1]			;вычесть половинку диаметра пятна
SUB	[X+2j+1]		;вычесть Y-координату центра пятна
JL	@@ActPointsStart


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

Давайте что ли поменяем знак выражения, чтобы "<" заменилось на "≥" которое как раз-таки нестрогое:
	Acc	[X+2j+1]
	DIV2A	[X+1]
	SUB	[SP+2j+k]
	JGE	@@ActPointsStart


Но теперь придётся переиначить и последующее условие: раньше мы там оказывались при результате выше нуля, и дополнительно вычитали D1, после чего повторно проверяли результат. Но теперь мы там окажемся при результате ниже нуля, D1 надо прибавлять, но флаг знака меняет только вычитание! Поэтому в этот раз придётся вычесть (-D1) - это ещё одна константа, ну и хрен бы с ней, справимся как-нибудь...

Было так:
;возможно, мы ещё не очень далеко ушли от пятна, и как оказывается, пятно здесь продолжается
SUB	D1
JGE	@@RemoveBlob	;уже далеко, пора его удалять!


А станет так:
SUB	nD1
JL	@@RemoveBlob


Ну и ещё одна константа, nD1 (со знаком минус), пока не зафигачим обработку математических выражений в компиляторе.

8. Убираем "человеческую" информацию

У меня должно было в символьном виде выводиться на UART сначала количество пятен, потом параметры каждого из них: координата, диаметр и яркость.

На это весьма и весьма много кода уходило, а в итоге всё равно пришлось дамп памяти получать и научиться его автоматически расшифровывать на ПК, давая "визуализацию". Так что весь этот код:

		;давайте передадим параметры обнаруженных точек по UART
		;пока тупо по младшему байту
		SIO	UART

.code
		X	PointsStr
		CALL	print
				
		Z	AllPoints	;В списке AllPoints нет фиктивных точек. Но может вообще никаких не быть!
		;Хотя сам AllPoints - это "заголовок", с него надо двинуться вперёд, если получится
		i	0		;номер точки (для отображения в терминале, поэтому нумерация с 1)
		JMP	@@moveOn	;перемещение на очередную точку
		;к началу цикла мы переместились на одну из найденных
@@outLoop:	X	NumSign		
		Acc	i
		CALL	CallThemAll
		;назвали номер точки, теперь выдаём её параметры

;в [Z+1] лежит радиус точки,
;в [Z+2j+k]=[Z+2] - X-координата
;в [Z+2j+1]=[Z+3] - Y-координата
;в [Z+4j+k]=[Z+4] - яркость
		X	XStr
		Acc	[Z+2j+k]		;X-координата
		CALL 	CallThemAll
		
		X	YStr
		Acc	[Z+2j+1]	;Y-координата
		CALL 	CallThemAll
				
		X	LumStr
		Acc	0xFFF		;инверсия 12 младших бит через вычитание
		SUB	[Z+4j+k]	;Яркость
		CALL 	CallThemAll
				
		X	SizeStr
		Acc	[Z+1]	;размер точки
		CALL 	CallThemAll				
				
				
			;продвигаемся на одну позицию, если только указатель не nil
@@moveOn:	ZAcc	RoundZero
		SUB	[Z+k]
		Z	[Z+k]
		i++	0
		JL	@@outLoop


можно "закомментировать".

А в придачу и эти строки:

	;после 3 вызовов этой процедуры начинается экономия :)
	CallThemAll	proc
		CALL	print
		CALL	BetterBin2Bcd
		OUT 	13
		OUT 	10
		JMP 	[--SP]
	CallThemAll endp
	
	%include "Bin2Bcd.asm"


что избавляет нас от одной из довольно крупных процедур BetterBin2Bcd (чтобы отобразить целые числа в десятичном виде).

Теперь вместо 258 слов данных у нас выходит 208, мы вернулись к родным 8 битам :)



На этом, как будто бы, всё. Осталось отсинтезировать - и посмотреть, что из всего этого выходит...
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