-
Всем доброго времени суток! Появилась такая проблема: необходимо изменить HSV изображения. С этим вроде окей - нашел формулы в википедии, постарался, перевел их (кажется, корректно) в язык и работает все. Но только через Pixels[x,y]. Это, безусловно, дико медленно, если изображение имеет размеры более 50х50 пикселей. В связи с этим делом решил изучить метод Scanline. Погуглил, нашел интересные сайты. Вроде бы понял, что это такое и с чем едят, но только не смог понять, как адаптировать полученные знания к текущей задаче ибо примеры давались довольно простенькие, но ничего специфичного не объяснялось). Код, работающий через Pixels[x, y]:
var
i,j: integer;
R,G,B: real;
H,S,V: real;
begin
with Bitmap do
begin
for i:=0 to Bitmap2.Width-1 do
for j:=0 to Bitmap2.Height-1 do
begin
ColorToRGB(Pixels[i,j],R,G,B);
RGBtoHSV(R,G,B,H,S,V);
S:=S*TrackBar6.Position/100;
V:=V*TrackBar7.Position/50;
H:=TrackBar5.Position;
HSVtoRGB(H,S,V,R,G,B);
Bitmap2.Canvas.Pixels[i,j]:=RGBtoColor(R,G,B);
end;
end;
Image2.Canvas.Draw(0, 0, Bitmap2);
"Переделанный" код под Scanline:
var
R,G,B: real;
H,S,V: real;
RowOut: PRGBArray;
x, y: integer;
AColor: integer;
_R, _G, _B: byte;
begin
Bitmap2.PixelFormat := pf24bit;
with Bitmap2.Canvas do
begin
for y:=0 to Bitmap2.Height-1 do
begin
RowOut := Bitmap2.ScanLine[y];
for x:=0 to Bitmap2.Width-1 do
begin
_R := GetRValue(RowOut[x].rgbtRed);
_G := GetGValue(RowOut[x].rgbtGreen);
_B := GetBValue(RowOut[x].rgbtBlue);
AColor := RGBToColor(_R, _G, _B);
ColorToRGB(AColor,R,G,B);
RGBtoHSV(R,G,B,H,S,V);
S:=S*TrackBar1.Position/100;
V:=V*TrackBar2.Position/50;
H:=TrackBar3.Position;
HSVtoRGB(H,S,V,R,G,B);
RowOut[x].rgbtRed := GetRValue(RGBToColor(R, G, B));
RowOut[x].rgbtGreen := GetGValue(RGBToColor(R, G, B));;
RowOut[x].rgbtBlue := GetBValue(RGBToColor(R, G, B));
end;
end;
end;
Image1.Canvas.Draw(0, 0, Bitmap2);
Самопальные процедуры:
procedure ColorToRGB(Color: integer; var R,G,B: real);
begin
R:=(Color and $0000FF)/255;
G:=((Color and $00FF00) shr 8)/255;
B:=((Color and $FF0000) shr 16)/255;
end;
function RGBtoColor(R,G,B: real): integer;
begin
Result:=Trunc(B*255) shl 16+Trunc(G*255) shl 8+Trunc(R*255);
end;
Собственно, не пойму, почему, когда я использую Scanline, то в пременные _R, _G, _B не передаются данные о цвете пикселя? Когда пытаюсь использовать процедуру изменения HSV со Scanline, то изображение пропадает и все (после теста я понял, что оно просто окрашивается белым цветом, получаемым из RGBToColor). Заранее спасибо за дельные советы.
-
_R := GetRValue(RowOut[x].rgbtRed); _G := GetGValue(RowOut[x].rgbtGreen); _B := GetBValue(RowOut[x].rgbtBlue); зачем дважды 1 метод применять? Либо одно либо другой
_R := RowOut[x].rgbtRed; _G :=RowOut[x].rgbtGreen; _B :=RowOut[x].rgbtBlue;
_R := GetRValue(RowOut[x]); _G := GetGValue(RowOut[x]); _B := GetBValue(RowOut[x]);
-
> > _R := GetRValue(RowOut[x]); > _G := GetGValue(RowOut[x]); > _B := GetBValue(RowOut[x]);
Получить составляющие цвета из указтеля на пикселы невозможно.
> _R := RowOut[x].rgbtRed; > _G :=RowOut[x].rgbtGreen; > _B :=RowOut[x].rgbtBlue;
Этот вариант не работает, ибо пробовал его. В принципе, только поэтому и задал вопрос здесь, на форуме
-
Немного странный код. Зачем-то GetRValue и "and $00FF00"... Как и откуда грузите картинку? Вот самое интересное откуда берутся Bitmap и Bitmap2 ?
-
> Немного странный код. Зачем-то GetRValue и "and $00FF00". > .. Как и откуда грузите картинку? > Вот самое интересное откуда берутся Bitmap и Bitmap2 ?
Битмапы создаются при создании формы:
BitMap:=TBitMap.Create;
BitMap2:=TBitMap.Create;
Bitmap2.Width:=Bitmap.Width;
Bitmap2.Height:=Bitmap.Height;
Картинку грузим при нажатии на кнопку "Открыть":
if OpenPictureDialog1.Execute then begin
BitMap.LoadFromFile(OpenPictureDialog1.FileName);
Image1.Canvas.Draw(0, 0, Bitmap);
Image2.Canvas.Draw(0, 0, Bitmap);
Bitmap2.Width:=Bitmap.Width;
Bitmap2.Height:=Bitmap.Height;
end;
Мне непонятно, почему, когда я вот тут:
_R := RowOut[x].rgbtRed;
_G := RowOut[x].rgbtGreen;
_B := RowOut[x].rgbtBlue;
пытаюсь присвоить цвет пикселей переменным, цвет всегда равен (255, 255, 255) независимо от изображения. Когда работа идет через Pixels[x,y], то, по идее, происходит то же самое, но используя Scanline, я этого не смог добиться. Однако, если я просто напишу:
RowOut[x].rgbtRed := 255;
RowOut[x].rgbtGreen := 255;
RowOut[x].rgbtBlue := 0;
то, закономерно, получу желтый цвет.
-
> пытаюсь присвоить цвет пикселей переменным, цвет всегда > равен (255, 255, 255) независимо от изображения.Когда работа > идет через Pixels[x,y], то, по идее, происходит то же самое, > но используя Scanline, я этого не смог добиться.Однако, > если я просто напишу:
Ну так всё правильно. Вы же из пустой картинке читаете. Поэтому там и 255. Вы же в Bitmap2 после создание ничего не положили. А при использование Pixels вы читаете из другой картинке из Bitmap. Надо просто скопировать в Bitmap в Btamp2 По хорошему лучше 2 указателя. RowIn и RowOut
for y:=0 to Bitmap2.Height-1 do
begin
RowIn := Bitmap.ScanLine[y];
RowOut := Bitmap2.ScanLine[y];
for x:=0 to Bitmap2.Width-1 do
begin
_R := RowIn[x].rgbtRed;
_G := RowIn[x].rgbtGreen;
_B := RowIn[x].rgbtBlue;
AColor := RGBToColor(_R, _G, _B);
ColorToRGB(AColor,R,G,B);
RGBtoHSV(R,G,B,H,S,V);
S:=S*TrackBar1.Position/100;
V:=V*TrackBar2.Position/50;
H:=TrackBar3.Position;
HSVtoRGB(H,S,V,R,G,B);
RowOut[x].rgbtRed := GetRValue(RGBToColor(R, G, B));
RowOut[x].rgbtGreen := GetGValue(RGBToColor(R, G, B));;
RowOut[x].rgbtBlue := GetBValue(RGBToColor(R, G, B));
end;
end;
-
И последние 3 строчки надо упростить, не нужно туда сюда конвертировать цвета.
RowOut[x].rgbtRed :=R RowOut[x].rgbtGreen := G; RowOut[x].rgbtBlue := B;
-
> Pavia
Спасибо ОГРОМНОЕ за помощь! Я немного переделал код, поскольку небольшая ошибка искажает изображение:
AColor := RGBToColor(_R, _G, _B);
и ее надо заменить на:
AColor := RGB(Round(R), Round(G), Round(B));
Также "усовершенствовал" эту часть:
RowOut[x].rgbtRed := GetRValue(RGBToColor(R, G, B));
RowOut[x].rgbtGreen := GetGValue(RGBToColor(R, G, B));;
RowOut[x].rgbtBlue := GetBValue(RGBToColor(R, G, B));
просто замените ее на это:
RowOut[x].rgbtRed := GetRValue(AColor);
RowOut[x].rgbtGreen := GetGValue(AColor);
RowOut[x].rgbtBlue := GetBValue(AColor);
К тому же, я избавился от переменных _R, _G, _B. Теперь все работает как часы. Собственно, сам работающий код:
var
R,G,B: real;
H,S,V: real;
RowOut, RowIn: PRGBArray;
x, y: integer;
AColor: integer;
begin
Bitmap.PixelFormat := pf24bit;
Bitmap2.PixelFormat := pf24bit;
with Bitmap do
begin
for y:=0 to Bitmap2.Height - 1 do
begin
RowIn := Bitmap.ScanLine[y];
RowOut := Bitmap2.ScanLine[y];
for x:=0 to Bitmap2.Width - 1 do
begin
R := RowIn[x].rgbtRed;
G := RowIn[x].rgbtGreen;
B := RowIn[x].rgbtBlue;
AColor := RGB(Round(R), Round(G), Round(B));
ColorToRGB(AColor, R, G, B);
RGBtoHSV(R, G, B, H, S, V);
S := S * TrackBar1.Position / 100; V := V * TrackBar2.Position / 50; H := TrackBar3.Position; HSVtoRGB(H, S, V, R, G, B);
AColor := RGBtoColor(R, G, B);
RowOut[x].rgbtRed := GetRValue(AColor);
RowOut[x].rgbtGreen := GetGValue(AColor);
RowOut[x].rgbtBlue := GetBValue(AColor);
end;
end;
end;
end;
Процедура и функция ColorToRGB и RGBToColor приведены в первом сообщении темы. Процедуры RGBToHSV и HSVToRGB можно найти либо в интернете, либо просто немного подумать и перевести формулы, которые есть в википедии. (даже я, далеко не математик осилил их :)). Всем спасибоза участие!
-
убери из циклов наружу TrackBar.Position
-
Плавающую точку нужно убрать и лишние преобразования (RGB/ColorToRGB). TrackBar.Position скорее с т.з. чистоты кода, а по скорости не так важно, там свойство без геттера.
-
> убери из циклов наружу TrackBar.Position
Да, исправил, спасибо за замечание. А какие могут быть негативные последствия оставления операций с расчетами TrackBar.Position's в цикле?
-
>А какие могут быть негативные последствия оставления операций с расчетами TrackBar.Position's в цикле? Замедление, иногда огромное
-
> Плавающую точку нужно убрать и лишние преобразования (RGB/ColorToRGB). > TrackBar.Position скорее с т.з. чистоты кода, а по скорости > не так важно, там свойство без геттера.
Так с ней вроде все окей, с плавающей точкой. А вот лишние преобразования убрал, Вы правы - они были совсем не к месту. Спасибо за указание!
-
Компилятор Дельфи не очень эффективно работает с плавающей точкой, поэтому целые почти всегда быстрее. Плюс ещё преобразования целые-вещественные занимают какое-то время. Сейчас проверил - преобразование RGB->HSV в 2 раза быстрее в целых. Из-за разных режимов округления результат немного отличается (на 1-2 единицы).
-
> Компилятор Дельфи не очень эффективно работает с плавающей > точкой, поэтому целые почти всегда быстрее. Плюс ещё преобразования > целые-вещественные занимают какое-то время.Сейчас проверил > - преобразование RGB->HSV в 2 раза быстрее в целых. Из-за > разных режимов округления результат немного отличается (на > 1-2 единицы).
А у вас какая скорость? У меня 40 тактов на пиксель.
-
Такты не считал и вообще не сильно упирался с оптимизацией, просто переделал функцию с efg2 на целые.
type
PFColor =^TFColor;
TFColor = packed record
b,g,r: Byte;
end;
procedure RGB2HSV(Src : PFColor; Out H, S, V : Integer);
Const HSV_MAX_MUL = 360 shl 8;
Var Min, Max, Diff : Integer;
begin
H := 0; S := 0; V := 0;
If (Src.r < Src.g) and (Src.r < Src.b) then Min := Src.r else
If (Src.g < Src.b) then Min := Src.g else Min := Src.b;
If (Src.r > Src.g) and (Src.r > Src.b) then Max := Src.r else
If (Src.g > Src.b) then Max := Src.g else Max := Src.b;
Diff := Max - Min;
If (Max <> 0) and (Diff <> 0) then begin
S := ((Diff shl 8) div Max);
If S > 255 then S := 255;
If Src.r = Max then
H := ((Src.g - Src.b) shl 8) div Diff
else If Src.g = Max then
H := $200 + ((Src.b - Src.r) shl 8) div Diff
else
H := $400 + ((Src.r - Src.g) shl 8) div Diff;
H := H * 60;
If H < 0 then Inc(H, HSV_MAX_MUL) else
If H >= HSV_MAX_MUL then Dec(H, HSV_MAX_MUL);
H := H shr 8;
V := Max;
end;
end;
-
В принципе, тема раскрыта, хорошие мысли есть - можно закрывать))) Всем спасибо!
|