Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何为24位位图使用扫描线属性?_Delphi_Image Processing - Fatal编程技术网

Delphi 如何为24位位图使用扫描线属性?

Delphi 如何为24位位图使用扫描线属性?,delphi,image-processing,Delphi,Image Processing,如何使用属性进行24位位图像素操作?为什么我更喜欢使用它而不是经常使用的属性?1。介绍 在这篇文章中,我将尝试解释仅用于24位位图像素格式的属性用法,以及您是否确实需要使用它。首先看看是什么让这个属性如此重要 2.扫描线与否。。。? 您可以问自己,为什么要使用像使用属性这样的技巧,而您只需使用它访问位图的像素。答案是,即使在相对较小的像素区域上执行像素修改,也会出现明显的性能差异 该属性在内部使用Windows API函数和,用于获取和设置设备上下文颜色值。该技术的性能缺陷在于,在修改像素颜色值

如何使用属性进行24位位图像素操作?为什么我更喜欢使用它而不是经常使用的属性?

1。介绍 在这篇文章中,我将尝试解释仅用于24位位图像素格式的属性用法,以及您是否确实需要使用它。首先看看是什么让这个属性如此重要

2.扫描线与否。。。? 您可以问自己,为什么要使用像使用属性这样的技巧,而您只需使用它访问位图的像素。答案是,即使在相对较小的像素区域上执行像素修改,也会出现明显的性能差异

该属性在内部使用Windows API函数和,用于获取和设置设备上下文颜色值。该技术的性能缺陷在于,在修改像素颜色值之前,通常需要获取像素颜色值,这在内部意味着调用上述两个Windows API函数。该属性将赢得这场比赛,因为它提供了对存储位图像素数据的内存的直接访问。直接内存访问比两个WindowsAPI函数调用要快

但是,这并不意味着财产是完全坏的,你应该避免在任何情况下使用它。例如,当您打算偶尔修改几个像素(不是很大的区域)时,可能就足够了。但是,当您要使用像素区域进行操作时,不要使用它

3.深入像素内部 3.1原始数据 位图的像素数据(现在我们称之为原始数据)可以想象为一维字节数组,包含每个像素的颜色分量的强度值序列。位图中的每个像素由固定的字节数组成,具体取决于使用的像素格式

例如,24位像素格式的每个颜色分量都有1个字节——用于红色、绿色和蓝色通道。下图说明了如何为这种24位位图想象原始数据字节数组。此处的每个彩色矩形代表一个字节:

3.2案例研究 假设您有一个24位位图3x2像素(宽度3px;高度2px),请记住它,因为我将尝试解释一些内部内容,并在其上显示属性使用原则。它之所以如此之小,仅仅是因为它需要足够的空间来进行内部的深度观察(对于那些拥有明亮视野的人来说,这里是png格式的绿色图像示例)↘ ↙ :-)

3.3像素组成

首先让我们看看我们位图图像的像素数据是如何被内部存储的,看看原始数据。下面的图像显示原始数据字节数组,在这里你可以看到我们的小位图的每个字节,在它的数组中有索引。你也可以注意到,3个字节的组是如何形成单个像素的,并且哪些坐标是TH的。位于位图上的ese像素:

同一视图的另一个视图提供了以下图像。每个框代表我们想象的位图中的一个像素。在每个像素中,您可以看到其坐标和3个字节组,以及原始数据字节数组中的索引:

4.与色彩共存 4.1.初始值 如我们所知,我们想象的24位位图中的像素由3个字节组成,每个颜色通道1个字节。当您在想象中创建此位图时,所有像素中的所有字节都已违背您的意愿初始化为最大字节值-255。这意味着所有通道现在都具有最大颜色强度:

当我们看一看,哪个颜色是从每个像素的这些初始通道值混合而来的,我们会看到我们的位图是。因此,当您在Delphi中创建一个24位位图时,它最初是白色的。白色默认情况下是每个像素格式的位图,但它们的初始原始数据字节值可能不同

5.扫描线的秘密生活 通过以上阅读,我希望您了解位图数据是如何存储在原始数据字节数组中的,以及如何从这些数据中形成单个像素。现在转到属性本身,以及如何在直接原始数据处理中有用

5.1.扫描线用途 这篇文章的主菜属性是一个只读索引属性,它返回指向原始数据字节数组的第一个字节的指针,该数组属于位图中的指定行。换句话说,我们请求访问给定行的原始数据字节数组,我们收到的是指向该数组第一个字节的指针此属性的x参数指定要获取其数据的行的基于0的索引

下图说明了我们的虚拟位图和通过使用不同行索引的属性获得的指针:

5.2.扫描线优势 因此,根据我们所知道的,我们可以总结,它给我们一个指向某个行数据字节数组的指针。使用原始数据的行数组,我们可以工作-我们可以读取或覆盖其字节,但只能在特定行的数组边界范围内:

好的,我们有一个特定行中每个像素的颜色强度数组。考虑到这种数组的迭代,在这个数组中循环一个字节并只调整一个像素的3个颜色部分中的一个是不太舒服的。最好是在每个迭代中循环像素并同时调整所有3个颜色字节-就像w就像我们过去一样

5.3.跳过像素 为了简化行数组循环,我们需要一个与像素数据匹配的结构。幸运的是,对于24位位图,有这样一个结构;在Delphi中翻译为
TRGBTriple
。简而言之,这个结构类似于这样(每个成员代表一个颜色通道的强度):

由于我试图容忍那些在2009年以下使用Delphi版本的人,并且因为它使代码更容易理解,所以我不会在迭代中使用指针算法,而是在下面的示例中使用一个固定长度的数组,带有指向它的指针(po
type
  TRGBTriple = packed record
    rgbtBlue: Byte;
    rgbtGreen: Byte;
    rgbtRed: Byte;
  end;
type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..4095] of TRGBTriple;
type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..4095] of TRGBTriple;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  Bitmap: TBitmap;
  Pixels: PRGBTripleArray;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.Width := 3;
    Bitmap.Height := 2;
    Bitmap.PixelFormat := pf24bit;
    // get pointer to the second row's raw data
    Pixels := Bitmap.ScanLine[1];
    // iterate our row pixel data array in a whole width
    for I := 0 to Bitmap.Width - 1 do
    begin
      Pixels[I].rgbtBlue := 0;
      Pixels[I].rgbtGreen := 0;
      Pixels[I].rgbtRed := 0;
    end;
    Bitmap.SaveToFile('c:\Image.bmp');
  finally
    Bitmap.Free;
  end;
end;
Luminance = 0.299 R + 0.587 G + 0.114 B
type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..4095] of TRGBTriple;

procedure GrayscaleBitmap(ABitmap: TBitmap);
var
  X: Integer;
  Y: Integer;
  Gray: Byte;
  Pixels: PRGBTripleArray;
begin
  // iterate bitmap from top to bottom to get access to each row's raw data
  for Y := 0 to ABitmap.Height - 1 do
  begin
    // get pointer to the currently iterated row's raw data
    Pixels := ABitmap.ScanLine[Y];
    // iterate the row's pixels from left to right in the whole bitmap width
    for X := 0 to ABitmap.Width - 1 do
    begin
      // calculate luminance for the current pixel by the mentioned formula
      Gray := Round((0.299 * Pixels[X].rgbtRed) +
        (0.587 * Pixels[X].rgbtGreen) + (0.114 * Pixels[X].rgbtBlue));
      // and assign the luminance to each color component of the current pixel
      Pixels[X].rgbtRed := Gray;
      Pixels[X].rgbtGreen := Gray;
      Pixels[X].rgbtBlue := Gray;
    end;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.LoadFromFile('c:\ColorImage.bmp');
    if Bitmap.PixelFormat <> pf24bit then
      raise Exception.Create('Incorrect bit depth, bitmap must be 24-bit!');
    GrayscaleBitmap(Bitmap);
    Bitmap.SaveToFile('c:\GrayscaleImage.bmp');
  finally
    Bitmap.Free;
  end;
end;