-
В общем суть проблемы такая: неправильно "размывается" изображение. Некогда я как-то спрашивал на форуме помощи по поводу реализации blur'а, и мне помогли. Правда сейчас, спустя некоторое время, я понял, что та реализация немного хромает. Конкретно у нее несколько недостатков: - низкая скорость работы; - края исходного изображения не обрабатываются. Я не силен в реализации различного рода фильтров, поэтому поискал готовые решения. Нашел на одном (немецком) сайте вполне приемлемый вариант, однако, мне показалось, что есть возможность реализацию немного "подправить" (+ получить новые знания в области работы с памятью). В принципе все прошло неплохо, но есть один нюанс: модифицированный вариант исходного кода "размывает" изображение только по оси Х, что дает интересный эффект, но не совсем полноценный blur. Если текст выше лень читать, то у меня есть кое-что покороче: помогите понять, в чем дело с модифицированным вариантом кода. Для сравнения я прикладываю оригинал (со всеми исходными комментариями) кода ниже. Оригинальный код
procedure TForm1.BmpGBlur(Bmp: TBitmap; radius: Single);
Type
TRGB = Packed Record b, g, r: Byte End;
TRGBs = Packed Record b, g, r: Single End;
TRGBArray = Array[0..0] of TRGB;
Var
MatrixRadius: Byte;
Matrix : Array[-100..100] of Single;
Procedure CalculateMatrix;
Var x: Integer; Divisor: Single;
Begin
radius:=radius+1; MatrixRadius:=Trunc(radius);
If Frac(radius)=0 Then Dec(MatrixRadius);
Divisor:=0;
For x:=-MatrixRadius To MatrixRadius Do Begin
Matrix[x]:=radius-abs(x);
Divisor:=Divisor+Matrix[x];
End;
For x:=-MatrixRadius To MatrixRadius Do
Matrix[x]:=Matrix[x]/Divisor;
End;
Var
BmpSL : ^TRGBArray;
BmpRGB : ^TRGB;
BmpCopy : Array of Array of TRGBs;
BmpCopyRGB : ^TRGBs;
R, G, B : Single;
BmpWidth, BmpHeight: Integer;
x, y, mx : Integer;
Begin
Bmp.PixelFormat:=pf24bit;
If radius<=0 Then radius:=1 Else If radius>99 Then radius:=99; CalculateMatrix;
BmpWidth:=Bmp.Width;
BmpHeight:=Bmp.Height;
SetLength(BmpCopy,BmpHeight,BmpWidth);
For y:=0 To Pred(BmpHeight) Do Begin
BmpSL:=Bmp.Scanline[y];
BmpCopyRGB:=@BmpCopy[y,0];
For x:=0 to Pred(BmpWidth) Do Begin
R:=0; G:=0; B:=0;
For Mx:=-MatrixRadius To MatrixRadius Do Begin
If x+mx<0 Then
BmpRGB:=@BmpSL^[0] Else If x+mx>=BmpWidth Then
BmpRGB:=@BmpSL^[Pred(BmpWidth)] Else
BmpRGB:=@BmpSL^[x+mx];
B:=B+BmpRGB^.b*Matrix[mx];
G:=G+BmpRGB^.g*Matrix[mx];
R:=R+BmpRGB^.r*Matrix[mx];
End;
BmpCopyRGB^.b:=B; BmpCopyRGB^.g:=G;
BmpCopyRGB^.r:=R;
Inc(BmpCopyRGB);
End;
End;
For y:=0 To Pred(BmpHeight) Do Begin
BmpRGB:=Bmp.ScanLine[y];
For x:=0 to Pred(BmpWidth) Do Begin
R:=0; G:=0; B:=0;
For mx:=-MatrixRadius To MatrixRadius Do Begin
If y+mx<=0 Then
BmpCopyRGB:=@BmpCopy[0,x] Else If y+mx>=BmpHeight Then
BmpCopyRGB:=@BmpCopy[Pred(BmpHeight),x] Else
BmpCopyRGB:=@BmpCopy[y+mx,x];
B:=B+BmpCopyRGB^.b*Matrix[mx];
G:=G+BmpCopyRGB^.g*Matrix[mx];
R:=R+BmpCopyRGB^.r*Matrix[mx];
End;
BmpRGB^.b:=Round(B);
BmpRGB^.g:=Round(G);
BmpRGB^.r:=Round(R);
Inc(BmpRGB);
End;
End;
End;
-
ПродолжениеМодифицированный кодКод в комментариях - это оригинальный код
procedure TForm1.BmpGBlur(Bmp: TBitmap; radius: single);
Type
TRGB = Packed Record b, g, r: Byte End;
TRGBs = Packed Record b, g, r: Single End;
TRGBArray = Array[0..0] of TRGB;
PArrayX = ^ArrayX;
ArrayX = array [0..0] of TRGBs;
Var
MatrixRadius: Byte;
Matrix : Array[-100..100] of Single;
Procedure CalculateMatrix;
Var x: Integer; Divisor: Single;
Begin
radius:=radius+1; MatrixRadius:=Trunc(radius);
If Frac(radius)=0 Then Dec(MatrixRadius);
Divisor:=0;
For x:=-MatrixRadius To MatrixRadius Do Begin
Matrix[x]:=radius-abs(x);
Divisor:=Divisor+Matrix[x];
End;
For x:=-MatrixRadius To MatrixRadius Do
Matrix[x]:=Matrix[x]/Divisor;
End;
Var
BmpSL : ^TRGBArray;
BmpRGB : ^TRGB;
BmpCopy : Array of Array of TRGBs;
BmpCopyRGB : ^TRGBs;
R, G, B : Single;
BmpWidth, BmpHeight: Integer;
x, y, mx : Integer;
arr, pRow : PArrayX;
Begin
Bmp.PixelFormat:=pf24bit;
If radius<=0 Then radius:=1 Else If radius>99 Then radius:=99; CalculateMatrix;
BmpWidth:=Bmp.Width;
BmpHeight:=Bmp.Height;
try
GetMem(Arr, (BmpWidth * BmpHeight) * SizeOf(TRGBs));
For y:=0 To Pred(BmpHeight) Do Begin
pRow := @Arr[Y * BmpWidth];
BmpSL:=Bmp.Scanline[y];
For x:=0 to Pred(BmpWidth) Do Begin
R:=0; G:=0; B:=0;
For Mx:=-MatrixRadius To MatrixRadius Do Begin
If x+mx<0 Then
BmpRGB:=@BmpSL^[0] Else If x+mx>=BmpWidth Then
BmpRGB:=@BmpSL^[Pred(BmpWidth)] Else
BmpRGB:=@BmpSL^[x+mx];
B:=B+BmpRGB^.b*Matrix[mx];
G:=G+BmpRGB^.g*Matrix[mx];
R:=R+BmpRGB^.r*Matrix[mx];
End;
pRow[x].b:=b; pRow[x].g:=G;
pRow[x].r:=R;
End;
End;
For y:=0 To Pred(BmpHeight) Do Begin
BmpRGB:=Bmp.ScanLine[y];
pRow := @Arr[Y * BmpWidth];
For x:=0 to Pred(BmpWidth) Do Begin
R:=0; G:=0; B:=0;
For mx:=-MatrixRadius To MatrixRadius Do Begin
If y+mx<=0 Then
BmpCopyRGB:=@pRow[0] Else If y+mx>=BmpHeight Then
BmpCopyRGB:=@pRow[Pred(BmpHeight)] Else
BmpCopyRGB:=@pRow[y+mx]; B:=B+BmpCopyRGB^.b*Matrix[mx];
G:=G+BmpCopyRGB^.g*Matrix[mx];
R:=R+BmpCopyRGB^.r*Matrix[mx];
End;
BmpRGB^.b:=Round(pRow[x].b);
BmpRGB^.g:=Round(pRow[x].g);
BmpRGB^.r:=Round(pRow[x].r);
Inc(BmpRGB);
End;
End;
finally
FreeMem(Arr, (BmpWidth * BmpHeight) * SizeOf(TRGBs));
end;
В итоге, использование GetMem дает небольшой прирост в скорости выполнения процедуры, но вот результат не совсем тот, на который я рассчитывал. Буду благодарен за любые подсказки в плане того, где кроется ошибка с применением GetMem. Спасибо всем откликнувшимся.
-
Вообще говоря, во втором цикле исправленного кода вычисленные переменные R, G, B явно не используются (сохранение результатов закомментировано); здравый смысл подсказывает, что блюр обсчитывается именно в них.
-
RWolfДобрый день! Действительно, допустил ошибку, исправил ее, но результат теперь еще хуже. Ниже 4-ре ссылки на изображения, чтобы визуально было понятнее о чем речь. Изображение 1 Оригинальное изображение, которое я "размываю" с целью тестирования https://yadi.sk/i/L9UrXpJK3SwVuV Изображение 2 Оригинал изображения, "размытый" с помощью исходного кода https://yadi.sk/i/tBRgdzB73SwVuZ Изображение 3 Оригинал изображения, "размытый" с помощью модифицированного кода до исправления моей ошибки, указанной в сообщении №2 https://yadi.sk/i/Or1LN2sr3SwVrC Изображение 4 Оригинал изображения, "размытый" с помощью модифицированного кода после исправления моей ошибки, указанной в сообщении №2 https://yadi.sk/i/gUGhH-Bo3SwejaВсе-таки никак не могу понять, что не так с кодом. Заменил ведь только SetLength на GetMem и все.
-
> Действительно, допустил ошибку, исправил ее, но результат > теперь еще хуже.
Так где исправленный код-то?
-
Styx Код в сообщении #1, раскомментировать три строки с BmpRGB^ в конце второго цикла и там же закомментировать последующие строки с BmpRGB^ := Round(pRow[x]). После скомпилировать, выполнить и получим результат как на Изображение 4, сообщение #3.
-
Сделать вы что конкретно хотите? Матрица 100 на 100?? Нафига такая? Вам надо скорость или качество? Или что? И если что - нельзя просто брать и "размывать" один единственный битмап сам в себе. Должно быть два битмапа - источник и приёмник. А по поводу "краёв" - это всем известная спорная ситуация, описана в любой хорошей книге по базовым алгоритмам графики - однозначного решения нет, три или четыре варианта как их обрабатывать.
-
Redmond
> Сделать вы что конкретно хотите?
Конкретно хочу понять, почему замена SetLength на GetMem прошла неудачно и изображение "размывается" только по горизонтали.
> Вам надо скорость или качество?
Хотелось бы и скорость и качество, но не в этом случае. Я и так попытался увеличить скорость работы кода путем применения функции GetMem, но каким-то однобоким получился результат.
> нельзя просто брать и "размывать" один единственный битмап > сам в себе
Не пойму, почему нельзя, но таки посмею заявить: в вышеприведенном коде содержимое битмапа копируется в буфер (создаваемый SetLength (в моем варианте - GetMem)) и только потом с ним идет работа.
> А по поводу "краёв"
А вот по поводу "краев" я ничего и не говорил. Проблема, если внимательно прочесть вопрос и посмотреть код, заключается в неправильном применении GetMem и дальнейшей работе с буфером (правда я так и не могу уразуметь, где же я там напортачил). Просто скомпилируйте программку с обоими вариантами кода и подсуньте ему свою картинку. Вы сразу поймете, о чем идет речь.
-
Просто вы упомянули:
> - края исходного изображения не обрабатываются.
Вот и пояснял. :) Если совсем кратко - они и не должны обрабатываться основным алгоритмом. Они обрабатываются отдельно, одним из модифицированных алгоритмов.
> в вышеприведенном коде
омм... Действительно. Недоглядел. Но скорее потому что это сделано странно и капец как не продумано на перспективу. :) Как локальная копия? Да ещё и в общем случае не идентичного размера? А копирование порциями, да даже не по 32 бита, а по 8 бит?? *готовит банку огурцов*
Это нужно не "чинить", а "половину переделывать". Попробую, ток чуть позже. А вам действительно надо MatrixRadius от 0 до 99 и Matrix 100 на 100? Более упрощённые версии размытия не подходят или чего?
-
Что-то не получается у меня найти время чтоб спокойно и полностью вчитаться в этот код. Там всё же используется "алгоритм обработки краёв" (хоть и не самый хороший) - там где подписано "erster Pixel" и "letzter Pixel", но вот почему он работает вместе с основным? Это плохая идея. И ещё фильтр там оказывается не полноценный, а упрощённый (Hor/Ver). Я с такими не работал, они обычно слишком врут. :) Но тут всего лишь размытие, может для него не так критично. Надо протестировать.
-
-
> Прикольно
это не тот код, который нельзя еще раз ускорить )
-
Redmond > Просто вы упомянули: > > > - края исходного изображения не обрабатываются. >
Да, действительно, упомянуть-то упомянул, но не пояснил. По поводу отсутствия обработки краев - это относилось к тому коду для "размытия", который я использовал ранее. Не к тому, который опубликован в данной теме. > сделано странно и капец как не продумано на перспективу. > :)
С этим не буду спорить- в обработке изображений не разбираюсь совсем, поэтому не могу судить насколько дальновиден был автор оригинального кода когда писал его. В конце концов - он же работает)). > А вам действительно надо MatrixRadius от 0 до 99 и Matrix > 100 на 100? Более упрощённые версии размытия не подходят > или чего?
Матрица, видимо, создается с запасом как раз под максимальный размер радиуса "размытия". Думаю, что максимальный радиус можно оставить в рамках 10 ед. (соответственно, матрица будет -10..+10). > но вот почему он работает вместе с основным? Это плохая > идея.
Как я и говорил ранее - вообще не разбираюсь в этом, но автору кода виднее.Если есть идеи по изменению кода в лучшую сторону - дерзайте, выложите их здесь. У меня таких идей нет. Точнее, попытался заменить SetLength на GetMem и что-то пошло совсем не так(( Плохиш > Прикольно
Действительно, забавная формулировка. Правда, исходный код я брал из другого форума: https://www.entwickler-ecke.de/viewtopic.php?sid=54f81316bc966742e70265bfe1a5900d&t=19270&view=dlSha > это не тот код, который нельзя еще раз ускорить )
Дорогу осилит идущий)) Я вот уже который день пытаюсь заставить модифицированный код "размывать" пиксели не только горизонтально, но и вертикально, но все без толку. Сдается мне, что придется использовать оригинальный вариант кода.
-
Imho, стоит разобраться не с кодом, а с алгоритмом. А вникать в чужой код, не понимая, что он делает - напрасная трата времени. Если Вам нужно размытие - то, как я понимаю, нужно делать свёртку с двумерным гауссианом. Чтобы работало быстрей, можно это делать в потоках - для каждого пикселя же расчёт независимый - или вообще на видеокарте. Я так это понимаю.
-
Добрый вечер, это снова я. В общем, разобрался с кодом и решил использовать оригинал - оптимизатор из меня все равно никакой))
|