简体   繁体   English

如何从 TBitmap 获取像素数组?

[英]How to get a pixel array from TBitmap?

In a camera application bitmap pixel arrays are retrieved from a streaming camera.在相机应用程序中,位图像素阵列是从流相机中检索的。 The pixel arrays are captured by writing them to a named pipe, where on the other end of the pipe, ffmpeg retrieves them and creates an AVI file.像素数组是通过将它们写入命名管道来捕获的,在管道的另一端,ffmpeg 检索它们并创建一个 AVI 文件。

I will need to create one custom frame (with custom text on), and pipe its pixels as the first frame in the resulting movie.我将需要创建一个自定义帧(带有自定义文本),并将其像素作为结果电影中的第一帧。

The question is how can I use a TBitmap (for convenience) to问题是我如何使用 TBitmap(为方便起见)来

  1. Create a X by Y monochrome (8 bit) bitmap from scratch, with custom text on.从头开始创建 X x Y 单色(8 位)位图,并启用自定义文本。 I want the background to be white, and the text to be black.我希望背景为白色,文本为黑色。 (Mostly figured this step out, see below.) (主要是想出了这一步,见下文。)

  2. Retrieve the pixel array that I can send/write to the pipe检索我可以发送/写入管道的像素阵列

Step 1: The following code creates a TBitmap and writes text on it:步骤 1:以下代码创建一个 TBitmap 并在其上写入文本:

int w = 658;
int h = 492;
TBitmap* bm = new TBitmap();
bm->Width = w;
bm->Height = h;
bm->HandleType = bmDIB;
bm->PixelFormat = pf8bit;

bm->Canvas->Font->Name = "Tahoma";
bm->Canvas->Font->Size = 8;

int textY = 10;
string info("some Text");

bm->Canvas->TextOut(10, textY, info.c_str());

The above basically concludes step 1.以上基本上完成了步骤1。

The writing/piping code expects a byte array with the bitmaps pixels;写入/管道代码需要一个带有位图像素的字节数组; eg例如

unsigned long numWritten;
WriteFile(mPipeHandle, pImage, size, &numWritten, NULL);

where pImage is a pointer to a unsigned char buffer (the bitmaps pixels), and the size is the length of this buffer.其中 pImage 是指向无符号字符缓冲区(位图像素)的指针,大小是该缓冲区的长度。

Update: Using the generated TBitmap and a TMemoryStream for transferring data to the ffmpeg pipeline does not generate the proper result.更新:使用生成的 TBitmap 和 TMemoryStream 将数据传输到 ffmpeg 管道不会生成正确的结果。 I get a distorted image with 3 diagonal lines on it.我得到一个扭曲的图像,上面有 3 条对角线。

The buffersize for the camera frame buffers that I receive are are exactly 323736 , which is equal to the number of pixels in the image, ie 658x492.我收到的相机帧缓冲区的缓冲区大小正好是323736 ,它等于图像中的像素数,即 658x492。 NOTE I have concluded that this 'bitmap' is not padded.注意我已经得出结论,这个“位图”没有被填充。 658 is not divisible by four. 658 不能被四整除。

The buffersize I get after dumping my generated bitmap to a memory stream, however, has the size 325798 , which is 2062 bytes larger than it is supposed to be.但是,将生成的位图转储到内存流后得到的 buffersize 大小为325798 ,比预期大2062字节。 As @Spektre pointed out below, this discrepancy may be padding?正如@Spektre 在下面指出的,这种差异可能是填充?

Using the following code for getting the pixel array;使用以下代码获取像素数组;

ByteBuffer CustomBitmap::getPixArray()
{
    // --- Local variables --- //
    unsigned int iInfoHeaderSize=0;
    unsigned int iImageSize=0;
    BITMAPINFO *pBitmapInfoHeader;

    unsigned char *pBitmapImageBits;

    // First we call GetDIBSizes() to determine the amount of
    // memory that must be allocated before calling GetDIB()
    // NB: GetDIBSizes() is a part of the VCL.
    GetDIBSizes(mTheBitmap->Handle,
                iInfoHeaderSize,
                iImageSize);

    // Next we allocate memory according to the information
    // returned by GetDIBSizes()
    pBitmapInfoHeader = new BITMAPINFO[iInfoHeaderSize];
    pBitmapImageBits = new unsigned char[iImageSize];

    // Call GetDIB() to convert a device dependent bitmap into a
    // Device Independent Bitmap (a DIB).
    // NB: GetDIB() is a part of the VCL.
    GetDIB(mTheBitmap->Handle,
            mTheBitmap->Palette,
            pBitmapInfoHeader,
            pBitmapImageBits);

    delete []pBitmapInfoHeader;

    ByteBuffer buf;
    buf.buffer = pBitmapImageBits;
    buf.size = iImageSize;
    return buf;
}

So final challenge seem to be to get a bytearray that has the same size as the ones coming from the camera.所以最后的挑战似乎是获得一个与来自相机的字节数组具有相同大小的字节数组。 How to find and remove the padding bytes from the TBitmap code??如何从 TBitmap 代码中查找和删除填充字节?

TBitmap has a PixelFormat property to set the bit depth. TBitmap有一个PixelFormat属性来设置位深度。

TBitmap has a HandleType property to control whether a DDB or a DIB is created. TBitmap有一个HandleType属性来控制是创建 DDB 还是 DIB。 DIB is the default. DIB 是默认设置。

Since you are passing BMPs around between different systems, you really should be using DIBs instead of DDBs, to avoid any corruption/misinterpretation of the pixel data.由于您在不同系统之间传递 BMP,您确实应该使用 DIB 而不是 DDB,以避免像素数据的任何损坏/误解。

Also, this line of code:另外,这行代码:

Image1->Picture->Bitmap->Handle = bm->Handle;

Should be changed to this instead:应该改为这样:

Image1->Picture->Bitmap->Assign(bm);
// or:
// Image1->Picture->Bitmap = bm;

Or this:或这个:

Image1->Picture->Assign(bm);

Either way, don't forget to delete bm;无论哪种方式,不要忘记delete bm; afterwards, since the TPicture makes a copy of the input TBitmap , it does not take ownership.之后,由于TPicture制作了输入TBitmap副本,因此它不会获得所有权。

To get the BMP data as a buffer of bytes, you can use the TBitmap::SaveToStream() method, saving to a TMemoryStream .要将 BMP 数据作为字节缓冲区获取,您可以使用TBitmap::SaveToStream()方法,保存到TMemoryStream Or, if you just want the pixel data, not the complete BMP data (ie, without BMP headers - see Bitmap Storage ), you can use the Win32 GetDiBits() function, which outputs the pixels in DIB format.或者,如果您只想要像素数据,而不是完整的 BMP 数据(即,没有 BMP 标头 - 请参阅位图存储),您可以使用 Win32 GetDiBits()函数,该函数以 DIB 格式输出像素。 You can't obtain a byte buffer of the pixels for a DDB, since they depend on the device they are rendered to.您无法获得 DDB 像素的字节缓冲区,因为它们取决于呈现它们的设备。 DDBs are only usable in-memory in conjunction with HDC s, you can't pass them around. DDB 只能在内存中与HDC结合使用,您不能传递它们。 But you can convert a DIB to a DDB once you have a final device to render it to.但是,一旦您拥有要渲染的最终设备,就可以将 DIB 转换为 DDB。

In other words, get the pixels from the camera, save them to a DIB, pass that around as needed (ie, over the pipe), and then do whatever you need with it - save to a file, convert to DDB to render onscreen, etc.换句话说,从相机获取像素,将它们保存到 DIB,根据需要传递它(即通过管道),然后用它做任何你需要的 - 保存到文件,转换为 DDB 以在屏幕上呈现, 等等。

This is just an addon to existing answer (with additional info after the OP edit)这只是现有答案的一个插件(在 OP 编辑​​后有附加信息)

Bitmap file-format has align bytes on each row (so there usually are some bytes at the end of each line that are not pixels) up to some ByteLength (present in bmp header).位图文件格式在每行上都有对齐字节(因此每行末尾通常有一些不是像素的字节)直到某个 ByteLength(存在于 bmp 标头中)。 Those create the skew and diagonal like lines.那些创造了倾斜和对角线一样的线条。 In your case the size discrepancy is 4 bytes per row:在您的情况下,大小差异为每行 4 个字节:

(xs + align)*ys  + header = size
(658+     4)*492 + 94     = 325798

but beware the align size depends on image width and bmp header ...但要注意对齐大小取决于图像宽度和 bmp 标题...

Try this instead:试试这个:

    // create bmp
    Graphics::TBitmap *bmp=new Graphics::TBitmap;
//  bmp->Assign(???);       // a) copy image from ???
    bmp->SetSize(658,492);  // b) in case you use Assign do not change resolution
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf8bit;
//  bmp->Canvas->Draw(0,0,???); // b) copy image from ???
    // here render your text using
    bmp->Canvas->Brush->Style=bsSolid;
    bmp->Canvas->Brush->Color=clWhite;
    bmp->Canvas->Font->Color=clBlack;
    bmp->Canvas->Font->Name = "Tahoma";
    bmp->Canvas->Font->Size = 8;
    bmp->Canvas->TextOutA(5,5,"Text");
    // Byte data
    for (int y=0;y<bmp->Height;y++)
     {
     BYTE *p=(BYTE*)bmp->ScanLine[y]; // pf8bit -> BYTE*
     // here send/write/store ... bmp->Width bytes from p[]
     }
//  Canvas->Draw(0,0,bmp);  // just renfder it on Form
    delete bmp; bmp=NULL;

mixing GDI winapi calls for pixel array access (bitblt etc...) with VCL bmDIB bitmap might cause problems and resource leaks (hence the error on exit) and its also slower then usage of ScanLine[] (if coded right) so I strongly advice to use native VCL functions (as I did in above example) instead of the GDI/winapi calls where you can.将像素阵列访问的 GDI winapi 调用(bitblt 等...)与 VCL bmDIB 位图混合可能会导致问题和资源泄漏(因此退出时出现错误),并且它的使用速度也比使用ScanLine[] (如果编码正确)慢,所以我强烈建议尽可能使用本机 VCL 函数(如我在上面的示例中所做的那样)而不是 GDI/winapi 调用。

for more info see:有关更多信息,请参阅:

Also you mention your image source is camera.您还提到您的图像来源是相机。 If you use pf8bit it mean its palette indexed color which is relatively slow and ugly if native GDI algo is used (to convert from true/hi color camera image) for better transform see:如果您使用pf8bit这意味着它的调色板索引颜色相对缓慢和丑陋,如果使用原生GDI算法(从真/高彩色相机图像转换)以获得更好的转换,请参见:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM