Форум: "Основная";
Текущий архив: 2006.01.29;
Скачать: [xml.tar.bz2];
ВнизStretchBlt на канву формы - искажение изображения Найти похожие ветки
← →
DVM © (2005-12-26 12:25) [0]Почему при выводе битмапа на канву формы через StretchBlt происходит искажение изображения - появляется какая то сетка (если рисовать надо меньше чем оригинал);
← →
Eraser © (2005-12-26 12:33) [1]
> DVM © (26.12.05 12:25)SetStretchBltMode(Canvas.Handle, HALFTONE);
// The docs say that you should call SetBrushOrgEx after SetStretchBltMode,
// but not what the arguments should be.
SetBrushOrgEx(Canvas.Handle, 0,0, nil);
StretchBlt...
← →
DVM © (2005-12-26 12:36) [2]
> Eraser © (26.12.05 12:33) [1]
Спасибо большое.
← →
Deka © (2005-12-26 18:25) [3]А еще можно посмотреть про функцию (не помню как называется) изменяющая Stretch-режим. Мне помнится удалось так победить искажения примерно в такой-же ситуации.
← →
Eraser © (2005-12-26 18:29) [4]
> Deka © (26.12.05 18:25) [3]
Не SetStretchBltMode случайно назывется? )
← →
GuAV © (2005-12-26 19:41) [5]Немного оффтопик, но... никто "вручную" Stretch не делал ? А то StretchBlt игнорирует SetStretchBltMode в 9х (используется COLORONCOLOR), а хотелось бы BLACKONWHITE для одного случая...
← →
Eraser © (2005-12-27 02:01) [6]
> GuAV © (26.12.05 19:41) [5]
Вручную не делал, но есть библиотеки, которые это умеют делать.
← →
Deka © (2005-12-27 10:32) [7]Да, именно так и называется (SetStrechBltMode). У меня есть процедура ручного strech, только называется это все ресамплингом, что есть правильно!
Попробуй разобраться...
unit Resample;
// -----------------------------------------------------------------------------
// Project: bitmap resampler
// Module: resample
// Description: Interpolated Bitmap Resampling using filters.
// Version: 01.02
// Release: 3
// Date: 15-MAR-1998
// Target: Win32, Delphi 2 & 3
// Author(s): anme: Anders Melander, anders@melander.dk
// Copyright (c) 1997,98 by Anders Melander
// Formatting: 2 space indent, 8 space tabs, 80 columns.
// -----------------------------------------------------------------------------
interface
uses
SysUtils, Classes, Graphics;
type
// Type of a filter for use with Stretch()
TFilterProc = function(Value: Single): Single;
// Sample filters for use with Stretch()
function SplineFilter(Value: Single): Single;
function BellFilter(Value: Single): Single;
function TriangleFilter(Value: Single): Single;
function BoxFilter(Value: Single): Single;
function HermiteFilter(Value: Single): Single;
function Lanczos3Filter(Value: Single): Single;
function MitchellFilter(Value: Single): Single;
// Interpolator
// Src: Source bitmap
// Dst: Destination bitmap
// filter: Weight calculation filter
// fwidth: Relative sample radius
procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single);
// -----------------------------------------------------------------------------
//
// List of Filters
//
// -----------------------------------------------------------------------------
const
ResampleFilters: array[0..6] of record
Name: string; // Filter name
Filter: TFilterProc;// Filter implementation
Width: Single; // Suggested sampling width/radius
end = (
(Name: "Box"; Filter: BoxFilter; Width: 0.5), // 0
(Name: "Triangle"; Filter: TriangleFilter; Width: 1.0), // 1
(Name: "Hermite"; Filter: HermiteFilter; Width: 1.0), // 2
(Name: "Bell"; Filter: BellFilter; Width: 1.5), // 3
(Name: "B-Spline"; Filter: SplineFilter; Width: 2.0), // 4
(Name: "Lanczos3"; Filter: Lanczos3Filter; Width: 3.0), // 5
(Name: "Mitchell"; Filter: MitchellFilter; Width: 2.0) // 6
);
IMPLEMENTATION
uses
math, dialogs;
// -----------------------------------------------------------------------------
//
// Filter functions
//
// -----------------------------------------------------------------------------
// Hermite filter
function HermiteFilter(Value: Single): Single;
begin
// f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
if (Value < 0.0) then Value := -Value;
if (Value < 1.0) then Result := (2.0 * Value - 3.0) * Sqr(Value) + 1.0
else Result := 0.0;
end;
// Box filter
// a.k.a. "Nearest Neighbour" filter
// anme: I have not been able to get acceptable
// results with this filter for subsampling.
function BoxFilter(Value: Single): Single;
begin
if (Value > -0.5) and (Value <= 0.5) then Result := 1.0
else Result := 0.0;
end;
// Triangle filter
// a.k.a. "Linear" or "Bilinear" filter
function TriangleFilter(Value: Single): Single;
begin
if (Value < 0.0) then Value := -Value;
if (Value < 1.0) then Result := 1.0 - Value
else Result := 0.0;
end;
// Bell filter
function BellFilter(Value: Single): Single;
begin
if (Value < 0.0) then Value := -Value;
if (Value < 0.5) then Result := 0.75 - Sqr(Value)
else if (Value < 1.5) then begin
Value := Value - 1.5;
Result := 0.5 * Sqr(Value);
end else
Result := 0.0;
end;
// B-spline filter
function SplineFilter(Value: Single): Single;
var
tt: single;
begin
if (Value < 0.0) then Value := -Value;
if (Value < 1.0) then begin
tt := Sqr(Value);
Result := 0.5*tt*Value - tt + 2.0 / 3.0;
end else if (Value < 2.0) then
begin
Value := 2.0 - Value;
Result := 1.0/6.0 * Sqr(Value) * Value;
end else
Result := 0.0;
end;
// Lanczos3 filter
function Lanczos3Filter(Value: Single): Single;
function SinC(Value: Single): Single;
begin
if (Value <> 0.0) then begin
Value := Value * Pi;
Result := sin(Value) / Value
end else
Result := 1.0;
end;
begin
if (Value < 0.0) then Value := -Value;
if (Value < 3.0) then Result := SinC(Value) * SinC(Value / 3.0)
else Result := 0.0;
end;
function MitchellFilter(Value: Single): Single;
const
B = (1.0 / 3.0);
C = (1.0 / 3.0);
var
tt: single;
begin
if (Value < 0.0) then Value := -Value;
tt := Sqr(Value);
if (Value < 1.0) then begin
Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * tt))
+ ((-18.0 + 12.0 * B + 6.0 * C) * tt)
+ (6.0 - 2 * B));
Result := Value / 6.0;
end else
if (Value < 2.0) then begin
Value := (((-1.0 * B - 6.0 * C) * (Value * tt))
+ ((6.0 * B + 30.0 * C) * tt)
+ ((-12.0 * B - 48.0 * C) * Value)
+ (8.0 * B + 24 * C));
Result := Value / 6.0;
end else
Result := 0.0;
end;
// -----------------------------------------------------------------------------
//
// Interpolator
//
// -----------------------------------------------------------------------------
type
TCListList = array of record
n: integer;
p: array of record
pixel : integer; // Source pixel
weight: single; // Pixel weight
end;
end;
TRGB = packed record
r, g, b: single;
end;
// Physical bitmap pixel
TColorRGB = packed record
r, g, b: BYTE;
end;
PColorRGB = ^TColorRGB;
// Physical bitmap scanline (row)
TRGBList = packed array[0..0] of TColorRGB;
PRGBList = ^TRGBList;
← →
Deka © (2005-12-27 10:33) [8]procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single);
var
xscale, yscale : single; // Zoom scale factors
i, j, k : integer; // Loop variables
center : single; // Filter calculation variables
width, fscale, weight : single; // Filter calculation variables
left, right : integer; // Filter calculation variables
n : integer; // Pixel number
Work : TBitmap;
contrib : TCListList;
rgb : TRGB;
color : TColorRGB;
SourceLine ,
DestLine : PRGBList;
// SourcePixel ,
DestPixel : PColorRGB;
Delta ,
DestDelta : integer;
SrcWidth ,
SrcHeight ,
DstWidth ,
DstHeight : integer;
function Color2RGB(Color: TColor): TColorRGB;
begin
Result.r:= Color AND $000000FF;
Result.g:= (Color AND $0000FF00) SHR 8;
Result.b:= (Color AND $00FF0000) SHR 16;
end;
function RGB2Color(Color: TColorRGB): TColor;
begin
Result:= Color.r OR (Color.g SHL 8) OR (Color.b SHL 16);
end;
begin
DstWidth:= Dst.Width; // Ширина приемника
DstHeight:= Dst.Height; // Высота приемника
SrcWidth:= Src.Width; // Ширина источника
SrcHeight:= Src.Height; // Высота источника
if (SrcWidth < 1) or (SrcHeight < 1) then
raise Exception.Create("Картинка-приемник слишком мала !");
// Create intermediate image to hold horizontal zoom
Work:= TBitmap.Create; // Создали рабочую картинку
try
Work.Height:= SrcHeight; // Высота рабочей картинки
Work.Width:= DstWidth; // Ширина рабочей картинки
Work.PixelFormat:= pf24bit; // Установили глубину цвета
// xscale := DstWidth / SrcWidth;
// yscale := DstHeight / SrcHeight;
// Improvement suggested by David Ullrich:
if (SrcWidth = 1) then xscale:= DstWidth / SrcWidth
else xscale:= (DstWidth - 1) / (SrcWidth - 1);
if (SrcHeight = 1) then yscale:= DstHeight / SrcHeight
else yscale:= (DstHeight - 1) / (SrcHeight - 1);
// This implementation only works on 24-bit images because it uses
// TBitmap.Scanline
If not (Src.PixelFormat=pf24bit) then ShowMessage("Проблема глубины цвета в источнике");
If not (Dst.PixelFormat=pf24bit) then ShowMessage("Проблема глубины цвета в приемнике");
// --------------------------------------------
// Pre-calculate filter contributions for a row
// -----------------------------------------------
SetLength(contrib, DstWidth);
// Horizontal sub-sampling - УМЕНЬШЕНИЕ
// Scales from bigger to smaller width
if (xscale < 1.0) then begin
width:= fwidth / xscale;
fscale:= 1.0 / xscale;
for i:= 0 to DstWidth-1 do begin
contrib[i].n:= 0;
SetLength(contrib[i].p, trunc(width * 2.0 + 1));
center:= i / xscale;
// Original code:
// left := ceil(center - width);
// right := floor(center + width);
left:= floor(center - width); // floor - округление в меньшую сторону
right:= ceil(center + width); // ceil - округление в большую сторону
for j:= left to right do begin
weight:= filter((center - j) / fscale) / fscale;
if (weight = 0.0) then continue;
if (j < 0) then n:= -j else
if (j >= SrcWidth) then n:= SrcWidth - j + SrcWidth - 1
else n:= j;
k:= contrib[i].n;
inc(contrib[i].n);
contrib[i].p[k].pixel:= n;
contrib[i].p[k].weight:= weight;
end;
end;
end else
// Horizontal super-sampling - УВЕЛИЧЕНИЕ
// Scales from smaller to bigger width
begin
for i := 0 to DstWidth-1 do begin
contrib[i].n:= 0;
SetLength(contrib[i].p, trunc(fwidth * 2.0 + 1));
center:= i/xscale;
// Original code:
// left := ceil(center - fwidth);
// right := floor(center + fwidth);
left:= floor(center - fwidth);
right:= ceil(center + fwidth);
for j:= left to right do begin
weight:= filter(center - j);
if (weight = 0.0) then continue;
if (j < 0) then n:= -j else
if (j >= SrcWidth) then n:= SrcWidth - j + SrcWidth - 1
else n:= j;
k:= contrib[i].n;
inc(contrib[i].n);
contrib[i].p[k].pixel:= n;
contrib[i].p[k].weight:= weight;
end;
end;
end;
← →
Deka © (2005-12-27 10:34) [9]// ----------------------------------------------------
// Apply filter to sample horizontally from Src to Work
// ----------------------------------------------------
for k:= 0 to SrcHeight-1 do begin
SourceLine:= Src.ScanLine[k]; // Источник
DestPixel:= Work.ScanLine[k]; // Приемник
for i:= 0 to DstWidth-1 do begin
rgb.r:= 0.0;
rgb.g:= 0.0;
rgb.b:= 0.0;
for j:= 0 to contrib[i].n-1 do begin
color:= SourceLine^[contrib[i].p[j].pixel];
weight:= contrib[i].p[j].weight;
if (weight = 0.0) then continue;
rgb.r:= rgb.r + color.r * weight;
rgb.g:= rgb.g + color.g * weight;
rgb.b:= rgb.b + color.b * weight;
end;
if (rgb.r > 255.0) then color.r:= 255 else
if (rgb.r < 0.0) then color.r:= 0
else color.r:= round(rgb.r);
if (rgb.g > 255.0) then color.g:= 255 else
if (rgb.g < 0.0) then color.g:= 0
else color.g:= round(rgb.g);
if (rgb.b > 255.0) then color.b:= 255 else
if (rgb.b < 0.0) then color.b:= 0
else color.b:= round(rgb.b);
// Set new pixel value
DestPixel^:= color;
// Move on to next column
inc(DestPixel);
end;
end;
// Free the memory allocated for horizontal filter weights
for i:= 0 to DstWidth-1 do Finalize(contrib[i].p);
Finalize(contrib);
// -----------------------------------------------
// Pre-calculate filter contributions for a column
// -----------------------------------------------
SetLength(contrib, DstHeight);
// Vertical sub-sampling - уменьшение
// Scales from bigger to smaller height
if (yscale < 1.0) then begin
width:= fwidth / yscale;
fscale:= 1.0 / yscale;
for i:= 0 to DstHeight-1 do begin
contrib[i].n:= 0;
SetLength(contrib[i].p, trunc(width * 2.0 + 1));
center:= i / yscale;
// Original code:
// left := ceil(center - width);
// right := floor(center + width);
left:= floor(center - width);
right:= ceil(center + width);
for j:= left to right do begin
weight:= filter((center - j) / fscale) / fscale;
if (weight = 0.0) then continue;
if (j < 0) then n:= -j else
if (j >= SrcHeight) then n:= SrcHeight - j + SrcHeight - 1
else n:= j;
k:= contrib[i].n;
inc(contrib[i].n);
contrib[i].p[k].pixel:= n;
contrib[i].p[k].weight:= weight;
end;
end
end else
// Vertical super-sampling
// Scales from smaller to bigger height
begin
for i:= 0 to DstHeight-1 do begin
contrib[i].n:= 0;
SetLength(contrib[i].p, trunc(fwidth * 2.0 + 1));
center:= i/yscale;
// Original code:
// left := ceil(center - fwidth);
// right := floor(center + fwidth);
left:= floor(center - fwidth);
right:= ceil(center + fwidth);
for j:= left to right do begin
weight:= filter(center - j);
if (weight = 0.0) then continue;
if (j < 0) then n:= -j else
if (j >= SrcHeight) then n:= SrcHeight - j + SrcHeight - 1
else n:= j;
k:= contrib[i].n;
inc(contrib[i].n);
contrib[i].p[k].pixel:= n;
contrib[i].p[k].weight:= weight;
end;
end;
end;
// --------------------------------------------------
// Apply filter to sample vertically from Work to Dst
// --------------------------------------------------
SourceLine:= Work.ScanLine[0];
Delta:= integer(Work.ScanLine[1]) - integer(SourceLine);
DestLine:= Dst.ScanLine[0];
DestDelta:= integer(Dst.ScanLine[1]) - integer(DestLine);
for k:= 0 to DstWidth-1 do begin
DestPixel:= pointer(DestLine);
for i:= 0 to DstHeight-1 do begin
rgb.r:= 0;
rgb.g:= 0;
rgb.b:= 0;
// weight := 0.0;
for j:= 0 to contrib[i].n-1 do begin
color:= PColorRGB(integer(SourceLine)+contrib[i].p[j].pixel*Delta)^;
weight:= contrib[i].p[j].weight;
if (weight = 0.0) then continue;
rgb.r:= rgb.r + color.r * weight;
rgb.g:= rgb.g + color.g * weight;
rgb.b:= rgb.b + color.b * weight;
end;
if (rgb.r > 255.0) then color.r:= 255 else
if (rgb.r < 0.0) then color.r:= 0
else color.r:= round(rgb.r);
if (rgb.g > 255.0) then color.g:= 255 else
if (rgb.g < 0.0) then color.g:= 0
else color.g:= round(rgb.g);
if (rgb.b > 255.0) then color.b:= 255 else
if (rgb.b < 0.0) then color.b:= 0
else color.b:= round(rgb.b);
DestPixel^:= color;
inc(integer(DestPixel), DestDelta);
// inc(DestPixel);
end;
Inc(SourceLine, 1);
Inc(DestLine, 1);
end;
// Free the memory allocated for vertical filter weights
for i:= 0 to DstHeight-1 do Finalize(contrib[i].p);
Finalize(contrib);
finally
FreeAndNil(Work);
end;
end;
end.
← →
Deka © (2005-12-27 10:38) [10]Как всешда есть место оптимизации... Удачи.
P.S. Основная идея ресамплинга сводится к тому, чтобы цветовую полоску (горизонтальную или вертикальную) превратить в аналоговую, а затем снова сделать дискретной с нужным количеством точек. Все работает в два прохода: горизонталь + вертикаль. Или наоборот - значения не имеет. И от того насколько качественно будет произведены операции преобразования в "аналоговое" изображение, а потом обратно зависит результат.
← →
GuAV © (2005-12-27 21:51) [11]
> Deka ©
Спасибо.
Код вроде примерно понятен (хотя не выполнял) - сначала формируется массив индексируемый по номеру пискселя в строке, элементы которого - массивы из составляющих его в новом изображении пикселей и их весов, затем этот массив применяется к каждой строке добавлением пикселя с учётом веса.
Мне таки придётся поискать ещё или писать наново, т.к. у меня не требуется разнообразия фильтров и вообще сложного фильтра, нужно только "ближайший соседний" (причём минимум - BLACKONWHITE), но нужна максимальная производительность и желательна работа с меньшей глубиной цвета. Т.е. при этом же алгоритме мой массив contrib.p будет содержать только номера пикселей, при его применении будет браться минимум из тех пикселей, вещественночисленная арифметика не нужна.
← →
Deka © (2005-12-28 13:27) [12]Модуль рабочий. По крайней мере у меня работал, пока в нем надобность не отпала. Убрать лишние фильтры и их поддержку, а потом интегрировать в алгоритм только один фильтр думаю несложно. Получится оптимизация. Потом если тебе надо только увеличивать (уменьшать), то можно выкинуть варианты по выбору увеличения (уменьшения).
← →
app © (2005-12-28 13:38) [13]Deka © (27.12.05 10:32) [7]
Для размещения больших кодов существуют разного рода кладовки, форум не кладовка.
← →
Deka © (2005-12-28 16:06) [14]Пардон.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2006.01.29;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.036 c