Создать CImage из байтового массива - PullRequest
2 голосов
/ 14 июля 2011

Мне нужно создать CImage из байтового массива (на самом деле, это массив unsigned char, но я могу привести к любой необходимой форме). Массив байтов имеет вид «RGBRGBRGB ...». Новое изображение должно содержать копию байтов изображения, а не использовать память самого байтового массива.

Я пробовал много разных способов достижения этой цели - в том числе проходил через различные функции создания HBITMAP, пытался использовать BitBlt - и пока ничего не получалось.

Чтобы проверить, работает ли функция, она должна пройти этот тест:

BYTE* imgBits;
int width;
int height;
int Bpp;   // BYTES per pixel (e.g. 3)
getImage(&imgBits, &width, &height, &Bpp); // get the image bits

// This is the magic function I need!!!
CImage img = createCImage(imgBits, width, height, Bpp);

// Test the image
BYTE* data = img.GetBits();  // data should now have the same data as imgBits

Все реализации createCImage() до сих пор заканчивались с data, указывающим на пустой (заполненный нулями) массив.

Ответы [ 4 ]

1 голос
/ 20 июня 2013

Вот более простое решение. Вы можете использовать GetPixelAddress (...) вместо всего этого BITMAPHEADERINFO и SedDIBitsToDevice. Другая проблема, которую я решил, была с 8-битными изображениями, для которых необходимо определить таблицу цветов.

CImage outImage;
outImage.Create(width, height, channelCount * 8);

int lineSize = width * channelCount;
if (channelCount == 1)
{
    // Define the color table
    RGBQUAD* tab = new RGBQUAD[256];
    for (int i = 0; i < 256; ++i)
    {
        tab[i].rgbRed = i;
        tab[i].rgbGreen = i;
        tab[i].rgbBlue = i;
        tab[i].rgbReserved = 0;
    }
    outImage.SetColorTable(0, 256, tab);
    delete[] tab;
}

// Copy pixel values
// Warining: does not convert from RGB to BGR
for ( int i = 0; i < height; i++ )
{
    void*       dst = outImage.GetPixelAddress(0, i);
    const void* src = /* put the pointer to the i'th source row here */;
    memcpy(dst, src, lineSize);
}
1 голос
/ 18 июля 2011

Используйте memcpy для копирования данных, затем SetDIBits или SetDIBitsToDevice в зависимости от того, что вам нужно сделать. Однако будьте осторожны, линии сканирования исходных данных изображения выровнены по 4-байтовым границам (IIRC, с тех пор, как я это сделал, прошло несколько лет), поэтому данные, которые вы получаете из GetDIBits, никогда не будут точно такими же, как исходные данные ( ну, может, в зависимости от размера изображения).

Так что, скорее всего, вам потребуется memcpy scanline по scanline.

1 голос
/ 20 июля 2011

Спасибо всем, мне удалось решить это в конце концов с вашей помощью.В основном это было связано с предложением @tinman и @ Roel использовать SetDIBitsToDevice(), но включало в себя немного дополнительных операций с битами и управления памятью, поэтому я решил поделиться своей конечной точкой здесь.

Вкод ниже, я предполагаю, что width, height и Bpp ( байт на пиксель) установлены, и что data является указателем на массив значений пикселей RGB.

// Create the header info
bmInfohdr.biSize = sizeof(BITMAPINFOHEADER);
bmInfohdr.biWidth = width;
bmInfohdr.biHeight = -height;
bmInfohdr.biPlanes = 1;
bmInfohdr.biBitCount = Bpp*8;
bmInfohdr.biCompression = BI_RGB;
bmInfohdr.biSizeImage = width*height*Bpp;
bmInfohdr.biXPelsPerMeter = 0;
bmInfohdr.biYPelsPerMeter = 0;
bmInfohdr.biClrUsed = 0;
bmInfohdr.biClrImportant = 0;

BITMAPINFO bmInfo;
bmInfo.bmiHeader = bmInfohdr;
bmInfo.bmiColors[0].rgbBlue=255;

// Allocate some memory and some pointers
unsigned char * p24Img = new unsigned char[width*height*3];
BYTE *pTemp,*ptr;
pTemp=(BYTE*)data;
ptr=p24Img;

// Convert image from RGB to BGR
for (DWORD index = 0; index < width*height ; index++)
{
    unsigned char r = *(pTemp++);
    unsigned char g = *(pTemp++);
    unsigned char b = *(pTemp++);   

    *(ptr++) = b;
    *(ptr++) = g;
    *(ptr++) = r;
}

// Create the CImage
CImage im;
im.Create(width, height, 24, NULL);

HDC dc = im.GetDC();
SetDIBitsToDevice(dc, 0,0,width,height,0,0, 0, height, p24Img, &bmInfo, DIB_RGB_COLORS);
im.ReleaseDC();

delete[] p24Img;
1 голос
/ 14 июля 2011

CImage поддерживает DIB довольно аккуратно и имеет метод SetPixel(), так что вы можете предположительно сделать что-то вроде этого (не скомпилированный, непроверенный код впереди!):

CImage img;
img.Create(width, height, 24 /* bpp */, 0 /* No alpha channel */);

int nPixel = 0;
for(int row = 0; row < height; row++)
{
    for(int col = 0; col < width; col++)
    {
        BYTE r = imgBits[nPixel++];
        BYTE g = imgBits[nPixel++];
        BYTE b = imgBits[nPixel++];
        img.SetPixel(row, col, RGB(r, g, b));
    }
}

Возможно, не самый эффективный метод, но я должен думать, что это самый простой подход.

...