How to save an image in Intel RealSense(Visual C++) - c++

I'm working on Intel RealSense SDK (R2). I want to save the image from Camera_viewer. I have worked till saving the frame to specific buffers and retrieving from the same. I want to know how to save those frames/images to a specified location/folder.
Here's my code:
PXCImage *colorIm, *depthIm;
for (int i=0; i<MAX_FRAMES; i++) {
// This function blocks until all streams are ready (depth and color)
// if false streams will be unaligned
if (psm->AcquireFrame(true)<PXC_STATUS_NO_ERROR) break;
// retrieve all available image samples
PXCCapture::Sample *sample = psm->QuerySample();
// retrieve the image or frame by type from the sample
colorIm = sample->color;
depthIm = sample->depth;
// render the frame
if (!renderColor->RenderFrame(colorIm)) break;
if (!renderDepth->RenderFrame(depthIm)) break;
// release or unlock the current frame to fetch the next frame
psm->ReleaseFrame();
}
I'm able to retrieve the frames/images successfully, but I want to save those files for further use. So I want to know how to save those files in a folder.
Thanks in advance

Same question was asked here and the answer that was posted solved the initial question. However there was a follow-up question on how to save the images to specific folder.
If you have that specific question, then answer would be the same SetFileName() only. According to this link, pxcCHAR *file is the full path of the file to playback or to be recorded. . That being said, you can create a custom folder and point your path to that custom folder followed by a valid file name to save your image.

To save the data I would create a Bitmap from the image data as ThorngardSO said and use the code from http://www.runicsoft.com/bmp.cpp to save it -
#include <windows.h>
#include <stdio.h> // for memset
bool SaveBMP ( BYTE* Buffer, int width, int height, long paddedsize, LPCTSTR bmpfile )
{
// declare bmp structures
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
// andinitialize them to zero
memset ( &bmfh, 0, sizeof (BITMAPFILEHEADER ) );
memset ( &info, 0, sizeof (BITMAPINFOHEADER ) );
// fill the fileheader with data
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paddedsize;
bmfh.bfOffBits = 0x36; // number of bytes to start of bitmap bits
// fill the infoheader
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = width;
info.biHeight = height;
info.biPlanes = 1; // we only have one bitplane
info.biBitCount = 24; // RGB mode is 24 bits
info.biCompression = BI_RGB;
info.biSizeImage = 0; // can be 0 for 24 bit images
info.biXPelsPerMeter = 0x0ec4; // paint and PSP use this values
info.biYPelsPerMeter = 0x0ec4;
info.biClrUsed = 0; // we are in RGB mode and have no palette
info.biClrImportant = 0; // all colors are important
// now we open the file to write to
HANDLE file = CreateFile ( bmpfile , GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ( file == NULL )
{
CloseHandle ( file );
return false;
}
// write file header
unsigned long bwritten;
if ( WriteFile ( file, &bmfh, sizeof ( BITMAPFILEHEADER ), &bwritten, NULL ) == false )
{
CloseHandle ( file );
return false;
}
// write infoheader
if ( WriteFile ( file, &info, sizeof ( BITMAPINFOHEADER ), &bwritten, NULL ) == false )
{
CloseHandle ( file );
return false;
}
// write image data
if ( WriteFile ( file, Buffer, paddedsize, &bwritten, NULL ) == false )
{
CloseHandle ( file );
return false;
}
// and close file
CloseHandle ( file );
return true;
}
This can then be subsequently loaded using more code from the same link -
/*******************************************************************
BYTE* LoadBMP ( int* width, int* height, long* size
LPCTSTR bmpfile )
The function loads a 24 bit bitmap from bmpfile,
stores it's width and height in the supplied variables
and the whole size of the data (padded) in <size>
and returns a buffer of the image data
On error the return value is NULL.
NOTE: make sure you [] delete the returned array at end of
program!!!
*******************************************************************/
BYTE* LoadBMP ( int* width, int* height, long* size, LPCTSTR bmpfile )
{
// declare bitmap structures
BITMAPFILEHEADER bmpheader;
BITMAPINFOHEADER bmpinfo;
// value to be used in ReadFile funcs
DWORD bytesread;
// open file to read from
HANDLE file = CreateFile ( bmpfile , GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
if ( NULL == file )
return NULL; // coudn't open file
// read file header
if ( ReadFile ( file, &bmpheader, sizeof ( BITMAPFILEHEADER ), &bytesread, NULL ) == false )
{
CloseHandle ( file );
return NULL;
}
//read bitmap info
if ( ReadFile ( file, &bmpinfo, sizeof ( BITMAPINFOHEADER ), &bytesread, NULL ) == false )
{
CloseHandle ( file );
return NULL;
}
// check if file is actually a bmp
if ( bmpheader.bfType != 'MB' )
{
CloseHandle ( file );
return NULL;
}
// get image measurements
*width = bmpinfo.biWidth;
*height = abs ( bmpinfo.biHeight );
// check if bmp is uncompressed
if ( bmpinfo.biCompression != BI_RGB )
{
CloseHandle ( file );
return NULL;
}
// check if we have 24 bit bmp
if ( bmpinfo.biBitCount != 24 )
{
CloseHandle ( file );
return NULL;
}
// create buffer to hold the data
*size = bmpheader.bfSize - bmpheader.bfOffBits;
BYTE* Buffer = new BYTE[ *size ];
// move file pointer to start of bitmap data
SetFilePointer ( file, bmpheader.bfOffBits, NULL, FILE_BEGIN );
// read bmp data
if ( ReadFile ( file, Buffer, *size, &bytesread, NULL ) == false )
{
delete [] Buffer;
CloseHandle ( file );
return NULL;
}
// everything successful here: close file and return buffer
CloseHandle ( file );
return Buffer;
}
You can then load those bitmap files at a later date using the code from Intel https://software.intel.com/sites/landingpage/realsense/camera-sdk/v1.1/documentation/html/manuals_image_and_audio_data.html -
// Image info
PXCImage::ImageInfo info={};
info.format=PXCImage::PIXEL_FORMAT_RGB32;
info.width=image_width;
info.height=image_height;
// Create the image instance
PXCImage image=session->CreateImage(&info);
// Write data
PXCImage::ImageData data;
image->AcquireAccess(PXCImage::ACCESS_WRITE,&data);
... // copy the imported image to data.planes[0]
image->ReleaseAccess(&data);
Using these three sets of code you should easily be able to save Bitmaps in any specified folder using WriteFile then, once loaded you can convert the bitmap back into ImageData.
Let me know how it goes.

Related

I need to create monocrome .bmp signature file by using C++ code but not able to create

I need to create monocrome .bmp signature file by using C++ code but not able to create. below is the code snippet.
bool SaveBMP ( BYTE* Buffer, int width, int height, long paddedsize, LPCTSTR bmpfile )
{
// declare bmp structures
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
// andinitialize them to zero
memset ( &bmfh, 0, sizeof (BITMAPFILEHEADER ) );
memset ( &info, 0, sizeof (BITMAPINFOHEADER ) );
// fill the fileheader with data
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paddedsize;
bmfh.bfOffBits = 0x36; // number of bytes to start of bitmap bits
// fill the infoheader
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = width;
info.biHeight = height;
info.biPlanes = 1; // we only have one bitplane
info.biBitCount = 1; // RGB mode is 24 bits
info.biCompression = BI_RGB;
info.biSizeImage = 0; // can be 0 for 24 bit images
info.biXPelsPerMeter = 0x0ec4; // paint and PSP use this values
info.biYPelsPerMeter = 0x0ec4;
info.biClrUsed = 0; // we are in RGB mode and have no palette
info.biClrImportant = 0; // all colors are important
// now we open the file to write to
HANDLE file = CreateFile ( bmpfile , GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ( file == NULL )
{
CloseHandle ( file );
return false;
}
// write file header
unsigned long bwritten;
if ( WriteFile ( file, &bmfh, sizeof ( BITMAPFILEHEADER ), &bwritten, NULL ) == false )
{
CloseHandle ( file );
return false;
}
// write infoheader
if ( WriteFile ( file, &info, sizeof ( BITMAPINFOHEADER ), &bwritten, NULL ) == false )
{
CloseHandle ( file );
return false;
}
// write image data
if ( WriteFile ( file, Buffer, paddedsize, &bwritten, NULL ) == false )
{
CloseHandle ( file );
return false;
}
// and close file
CloseHandle ( file );
return true;
}
**//when I run this code the created bitmap file is blank, signature is not being shown, only the background of signature file is able to show. **
Monochrom bitmap requires a palette. In this case the palette size is 8 bytes (2 RGBQUAD values)
This extra 8 bytes should be added to BITMAPFILEHEADER file size.
After BITMAPFILEHEADER, you are working with BITMAPINFOHEADER which does not contain information about palettes. You have to use BITMAPINFO directly instead of BITMAPINFOHEADER
BITMAPINFO* bmpinfo = (BITMAPINFO*)malloc(palette_size + sizeof(BITMAPINFOHEADER));
bmpinfo->bmiHeader = { sizeof(BITMAPINFOHEADER),
width, height, 1, bitcount, BI_RGB, 0, 0, 0, 0, 0 };
If Buffer was not retrieved for a monochrome bitmap then you cannot simply change bitcount in this save function and expect everything to work.
if ( file == NULL )// <-error
{
CloseHandle ( file );
return false;
}
If CreateFile fails, the return value is INVALID_HANDLE_VALUE. You can find out these values from online documentation for API functions. The corrected version should be
if(file != INVALID_HANDLE_VALUE)
{
unsigned long bwritten;
WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL);
WriteFile(file, bmpinfo, 8 + sizeof(BITMAPINFOHEADER), &bwritten, NULL);
WriteFile(file, Buffer, paddedsize, &bwritten, NULL);
CloseHandle(file);
return true;
}
return false;

C++ bitmap to base64

I have the following code to create a bitmap:
//raw data
PBYTE firstPixel = (PBYTE)((PBYTE)AnsiBdbRecord) + sizeof(WINBIO_BDB_ANSI_381_RECORD);
// declare other bmp structures
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
RGBQUAD rq[256];
// create the grayscale palette
for (int i = 0; i<256; i++)
{
rq[i].rgbBlue = i;
rq[i].rgbGreen = i;
rq[i].rgbRed = i;
rq[i].rgbReserved = 0;
}
//RGBQUAD bl = { 0,0,0,0 }; //black color
//RGBQUAD wh = { 0xff,0xff,0xff,0xff }; // white color
// andinitialize them to zero
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
memset(&info, 0, sizeof(BITMAPINFOHEADER));
// fill the fileheader with data
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // + padding;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD);
// fill the infoheader
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = Width;
info.biHeight = Height;
info.biPlanes = 1; // we only have one bitplane
info.biBitCount = PixelDepth; // RGB mode is 24 bits
info.biCompression = BI_RGB;
info.biSizeImage = 0; // can be 0 for 24 bit images
info.biXPelsPerMeter = 0x0ec4; // paint and PSP use this values
info.biYPelsPerMeter = 0x0ec4;
info.biClrUsed = 0; // we are in RGB mode and have no palette
info.biClrImportant = 0; // all colors are importantenter code here
And I save it as follows:
HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// write file header
if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// write infoheader
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
//write rgbquad for black
if (WriteFile(file, &rq, sizeof(rq), &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// write image data
if (WriteFile(file, &firstPixel[0], imageSize, &bwritten, NULL) == false)
{
DWORD dw = GetLastError();
CloseHandle(file);
}
// and clean up
CloseHandle(file);
I think the above is the standard way of saving bitmap images. However, instead of saving the image, I want it to be available as BASE64 and pass it in a HTTP Post. Therefore, this question relates to this one, but I am having a lot of difficulties converting the bmp structure to BASE64. I have taken the BASE64 encoder from here, but I have no idea how to pass the BMPFILEHEADER, BMPINFOHEADER, RGBQUAD, and raw data structure as a parameter to the BASE64 encoder.
Any thoughts or pointers on how to combine the information I gathered?
UPDATE
Thanks to Roman Pustylnikov, I have gotten a bit farther already:
I'm creating a struct like this:
struct ImageBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFOHEADER infobmp;
RGBQUAD rgb[256];
PBYTE bitmap;
};
Fill it as follows:
ImageBuffer capture;
capture.bfheader = bmfh;
capture.infobmp = info;
// create the grayscale palette
for (int i = 0; i<256; i++)
{
capture.rgb[i].rgbBlue = i;
capture.rgb[i].rgbGreen = i;
capture.rgb[i].rgbRed = i;
capture.rgb[i].rgbReserved = 0;
}
capture.bitmap = firstPixel;
And convert it as follows:
int totalSize = sizeof(capture.bfheader) + sizeof(capture.infobmp) + sizeof(capture.rgb) + imageSize;
std::string encodedImage = base64_encode(reinterpret_cast<const unsigned char*>(&capture), totalSize);
However, it gives me an invalid bitmap. Also, when I load the bitmap from disk (the one that is generated with writefile), I get a different base64 string. I use C# code to compare the two Base64 strings:
// generated base64 string
string test = "Put base64string generated from C++ here";
byte[] imageBytes = Convert.FromBase64String(test);
// generate the same string based on the actual bmp
byte[] data = File.ReadAllBytes(#"c:\successtest.bmp");
string original = Convert.ToBase64String(data);
UPDATE TWO: solution
The solution can be found in the latest update of Roman Pustylnikov's answer.
You can do it without file in the middle, using the following example as encoder/decoder:
The first approach is to create the contiguous memory and put it as an input:
int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + imageSize ;
unsigned char * bmpBuff = new char[size];
int i = 0;
memcpy((void *)( &( bmpBuff[i] )), (void *) &bfheader,sizeof(BITMAPFILEHEADER) );
i+=sizeof(BITMAPFILEHEADER);
memcpy((void *)( &( bmpBuff[i] )), (void *) &infobmp,sizeof(BITMAPINFOHEADER) );
i+=sizeof(BITMAPINFOHEADER);
memcpy((void *)( &( bmpBuff[i] )), (void *) &rq,sizeof(RGBQUAD)*256 );
i+=sizeof(RGBQUAD)*256;
memcpy((void *)( &( bmpBuff[i] )), (void *) firstPixel, imageSize );
std::string encodedImage = base64_encode(bmpBuff, size);
The cons of this approach is that you need to duplicate the memory.
Another approach is to handle the "lost triplet". For this we'll need to define a structure:
struct ScreenShotBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFO infobmp;
RGBQUAD rgb[256];
};
Now here comes the tricky part. Since the encoding handles the triples of bytes, you need to handle the border between two buffers (I haven't tested this so it might contain bugs, just a general approach).
int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256;
unsigned char *info = einterpret_cast<const unsigned char*> &screenshotInfo;
unsigned char *img = einterpret_cast<const unsigned char*> firstPixel;
std::string encodedInfo = base64_encode(info , size);
std::string encodedLostTriplet = "";
int offset = size%3;
unsigned char lostTriplet[3];
if (offset) {
lostTriplet[0]=info[size-offset];
if (offset==2)
lostTriplet[1] = info[size-offset+1];
else
lostTriplet[1] = img[0];
lostTriplet[2] = img[2-offset];
encodedLostTriplet = base64_encode(lostTriplet, 3);
}
else {
offset=3;
}
std::string encodedData = base64_encode(reinterpret_cast<const unsigned char*> &img[3-offset], imageSize - (3 - offset) );
std::string encodedImage = encodedInfo + lostTriplet + encodedData;
A little bit messy but should work on the same memory.

C++ How to create a bitmap file

I am trying to figure out how to create a bitmap file in C++ VS. Currently I have taken in the file name and adding the ".bmp" extension to create the file. I want to know how I could change the pixels of the file by making it into different colors or patterns (ie. like a checkerboard) This is my function that I have and I believe that I have to send 3 different Bytes at a time in order to establish the color of the pixel.
void makeCheckerboardBMP(string fileName, int squaresize, int n) {
ofstream ofs;
ofs.open(fileName + ".bmp");
writeHeader(ofs, n, n);
for(int row = 0; row < n; row++) {
for(int col = 0; col < n; col++) {
if(col % 2 == 0) {
ofs << 0;
ofs << 0;
ofs << 0;
} else {
ofs << 255;
ofs << 255;
ofs << 255;
}
}
}
}
void writeHeader(ostream& out, int width, int height){
if (width % 4 != 0) {
cerr << "ERROR: There is a windows-imposed requirement on BMP that the width be a
multiple of 4.\n";
cerr << "Your width does not meet this requirement, hence this will fail. You can fix
this\n";
cerr << "by increasing the width to a multiple of 4." << endl;
exit(1);
}
BITMAPFILEHEADER tWBFH;
tWBFH.bfType = 0x4d42;
tWBFH.bfSize = 14 + 40 + (width*height*3);
tWBFH.bfReserved1 = 0;
tWBFH.bfReserved2 = 0;
tWBFH.bfOffBits = 14 + 40;
BITMAPINFOHEADER tW2BH;
memset(&tW2BH,0,40);
tW2BH.biSize = 40;
tW2BH.biWidth = width;
tW2BH.biHeight = height;
tW2BH.biPlanes = 1;
tW2BH.biBitCount = 24;
tW2BH.biCompression = 0;
out.write((char*)(&tWBFH),14);
out.write((char*)(&tW2BH),40);
}
These are the two functions I am using for my code (one greyscale, one RGB saving).
Might give you a hint whats going wrong.
Note: they are done to work, not to be efficient.
void SaveBitmapToFile( BYTE* pBitmapBits, LONG lWidth, LONG lHeight,WORD wBitsPerPixel, LPCTSTR lpszFileName )
{
RGBQUAD palette[256];
for(int i = 0; i < 256; ++i)
{
palette[i].rgbBlue = (byte)i;
palette[i].rgbGreen = (byte)i;
palette[i].rgbRed = (byte)i;
}
BITMAPINFOHEADER bmpInfoHeader = {0};
// Set the size
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
// Bit count
bmpInfoHeader.biBitCount = wBitsPerPixel;
// Use all colors
bmpInfoHeader.biClrImportant = 0;
// Use as many colors according to bits per pixel
bmpInfoHeader.biClrUsed = 0;
// Store as un Compressed
bmpInfoHeader.biCompression = BI_RGB;
// Set the height in pixels
bmpInfoHeader.biHeight = lHeight;
// Width of the Image in pixels
bmpInfoHeader.biWidth = lWidth;
// Default number of planes
bmpInfoHeader.biPlanes = 1;
// Calculate the image size in bytes
bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel/8);
BITMAPFILEHEADER bfh = {0};
// This value should be values of BM letters i.e 0x4D42
// 0x4D = M 0×42 = B storing in reverse order to match with endian
bfh.bfType = 'B'+('M' << 8);
// <<8 used to shift ‘M’ to end
// Offset to the RGBQUAD
bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + sizeof(RGBQUAD) * 256;
// Total size of image including size of headers
bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;
// Create the file in disk to write
HANDLE hFile = CreateFile( lpszFileName,GENERIC_WRITE, 0,NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
if( !hFile ) // return if error opening file
{
return;
}
DWORD dwWritten = 0;
// Write the File header
WriteFile( hFile, &bfh, sizeof(bfh), &dwWritten , NULL );
// Write the bitmap info header
WriteFile( hFile, &bmpInfoHeader, sizeof(bmpInfoHeader), &dwWritten, NULL );
// Write the palette
WriteFile( hFile, &palette[0], sizeof(RGBQUAD) * 256, &dwWritten, NULL );
// Write the RGB Data
if(lWidth%4 == 0)
{
WriteFile( hFile, pBitmapBits, bmpInfoHeader.biSizeImage, &dwWritten, NULL );
}
else
{
char* empty = new char[ 4 - lWidth % 4];
for(int i = 0; i < lHeight; ++i)
{
WriteFile( hFile, &pBitmapBits[i * lWidth], lWidth, &dwWritten, NULL );
WriteFile( hFile, empty, 4 - lWidth % 4, &dwWritten, NULL );
}
}
// Close the file handle
CloseHandle( hFile );
}
void SaveBitmapToFileColor( BYTE* pBitmapBits, LONG lWidth, LONG lHeight,WORD wBitsPerPixel, LPCTSTR lpszFileName )
{
BITMAPINFOHEADER bmpInfoHeader = {0};
// Set the size
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
// Bit count
bmpInfoHeader.biBitCount = wBitsPerPixel;
// Use all colors
bmpInfoHeader.biClrImportant = 0;
// Use as many colors according to bits per pixel
bmpInfoHeader.biClrUsed = 0;
// Store as un Compressed
bmpInfoHeader.biCompression = BI_RGB;
// Set the height in pixels
bmpInfoHeader.biHeight = lHeight;
// Width of the Image in pixels
bmpInfoHeader.biWidth = lWidth;
// Default number of planes
bmpInfoHeader.biPlanes = 1;
// Calculate the image size in bytes
bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel/8);
BITMAPFILEHEADER bfh = {0};
// This value should be values of BM letters i.e 0x4D42
// 0x4D = M 0×42 = B storing in reverse order to match with endian
bfh.bfType = 'B'+('M' << 8);
// <<8 used to shift ‘M’ to end
// Offset to the RGBQUAD
bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
// Total size of image including size of headers
bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;
// Create the file in disk to write
HANDLE hFile = CreateFile( lpszFileName,GENERIC_WRITE, 0,NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
if( !hFile ) // return if error opening file
{
return;
}
DWORD dwWritten = 0;
// Write the File header
WriteFile( hFile, &bfh, sizeof(bfh), &dwWritten , NULL );
// Write the bitmap info header
WriteFile( hFile, &bmpInfoHeader, sizeof(bmpInfoHeader), &dwWritten, NULL );
// Write the palette
//WriteFile( hFile, &palette[0], sizeof(RGBQUAD) * 256, &dwWritten, NULL );
// Write the RGB Data
if(lWidth%4 == 0)
{
WriteFile( hFile, pBitmapBits, bmpInfoHeader.biSizeImage, &dwWritten, NULL );
}
else
{
char* empty = new char[ 4 - lWidth % 4];
for(int i = 0; i < lHeight; ++i)
{
WriteFile( hFile, &pBitmapBits[i * lWidth], lWidth, &dwWritten, NULL );
WriteFile( hFile, empty, 4 - lWidth % 4, &dwWritten, NULL );
}
}
// Close the file handle
CloseHandle( hFile );
}
Given your writeHeader is properly implemented this is almost correct. You need to fix 2 issues though:
You are writing one int per color channel. This should be one byte instead. You need to cast the literals to unsigned char.
Scanlines in bitmaps need to be DWORD-aligned. After your inner loop over col you need to write additional bytes to account for this, unless the size in bytes of the row is a multiple of four.
You need to force the output to be written in binary format, not text, this is chosen when you open your file/create your stream and to output all the values as bytes, not integers, this can be done in a number of ways possibly the easiest being write chr(0) or chr(255) - you also need to start your file with a header section - there are a number of formats that make this too long to go into in an answer here - some of them are down to preference as much as anything. There is a good summary in Wikipedia.
Basically you have to inform the receiving applications which format you are using, the number of rows, columns and how the colours are stored.

SHGetFileInfo not return the icon location

I try to extract the icon of file and return it to GetIconLocation of shell extension.
in general I change the icons of files (te.docx.xx) with the extension of xx and returns the icon of file without the xx. (for this I cretae temp file in temp directory with the original extension e.g te.docx)
my operating system is windows 7 x64.
my code is:
STDMETHODIMP CTxtIconShlExt::GetIconLocation (
UINT uFlags, LPTSTR szIconFile, UINT cchMax,
int* piIndex, UINT* pwFlags )
{
DWORD dwFileSizeLo, dwFileSizeHi;
DWORDLONG qwSize;
HANDLE hFile;
OutputDebugStringW(L"Hello world, from GetIconLocation !");
std::string strFilePath;
std::string tempFolder="c:\\.tmp";
std::string tempFile="tmpfile";
std::string fileWithOutDN;
SHFILEINFO retShFileInfo;
for(int i = 0; m_szFilename[i] != 0; i++)
{
strFilePath += m_szFilename[i];
}
fileWithOutDN= strFilePath.substr(0,strFilePath.size()-3 );
std::string extension = fileWithOutDN.substr(fileWithOutDN.find_last_of("."));
CreateDirectory(tempFolder.c_str(),NULL);
tempFile=tempFolder+"\\"+tempFile+extension;
GetFileAttributes(tempFile.c_str()); // from winbase.h
if(INVALID_FILE_ATTRIBUTES == GetFileAttributes(tempFile.c_str()) && GetLastError()==ERROR_FILE_NOT_FOUND)
{
//File not found
HANDLE h = CreateFile(tempFile.c_str(), // name of the file
GENERIC_WRITE, // open for writing
0, // sharing mode, none in this case
0, // use default security descriptor
CREATE_ALWAYS, // overwrite if exists
FILE_ATTRIBUTE_NORMAL,
0);
if (h)
{
CloseHandle(h);
}else
{
return S_FALSE; //faild to create file
}
}
ZeroMemory(&retShFileInfo, sizeof(SHFILEINFO));
CoInitialize(NULL);
SHGetFileInfo(tempFile.c_str(),256,&retShFileInfo,sizeof(SHFILEINFO),SHGFI_ICON | SHGFI_LARGEICON);
lstrcpyn ( szIconFile, retShFileInfo.szDisplayName, cchMax );
*piIndex = retShFileInfo.iIcon;
*pwFlags = 0;
return S_OK;
my problem is that the retShFileInfo.szDisplayName return an empty array (all items are zero), it should return full path to icon location.
I try to play with the combination of the flags but nothing was helpful

How to get width and height of directshow webcam video stream

I found a bit of code that gets me access to the raw pixel data from my webcam. However I need to know the image width, height, pixel format and preferably the data stride(pitch, memory padding or whatever you want to call it) if its ever gonna be something other than the width * bytes per pixel
#include <windows.h>
#include <dshow.h>
#pragma comment(lib,"Strmiids.lib")
#define DsHook(a,b,c) if (!c##_) { INT_PTR* p=b+*(INT_PTR**)a; VirtualProtect(&c##_,4,PAGE_EXECUTE_READWRITE,&no);\
*(INT_PTR*)&c##_=*p; VirtualProtect(p, 4,PAGE_EXECUTE_READWRITE,&no); *p=(INT_PTR)c; }
// Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ;
HRESULT __stdcall Receive ( void* inst, IMediaSample *smp ) {
BYTE* buf; smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
//AM_MEDIA_TYPE* info;
//smp->GetMediaType(&info);
HRESULT ret = Receive_ ( inst, smp );
return ret;
}
int WINAPI WinMain(HINSTANCE inst,HINSTANCE prev,LPSTR cmd,int show){
HRESULT hr = CoInitialize(0); MSG msg={0}; DWORD no;
IGraphBuilder* graph= 0; hr = CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,IID_IGraphBuilder, (void **)&graph );
IMediaControl* ctrl = 0; hr = graph->QueryInterface( IID_IMediaControl, (void **)&ctrl );
ICreateDevEnum* devs = 0; hr = CoCreateInstance (CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC, IID_ICreateDevEnum, (void **) &devs);
IEnumMoniker* cams = 0; hr = devs?devs->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &cams, 0):0;
IMoniker* mon = 0; hr = cams->Next (1,&mon,0); // get first found capture device (webcam?)
IBaseFilter* cam = 0; hr = mon->BindToObject(0,0,IID_IBaseFilter, (void**)&cam);
hr = graph->AddFilter(cam, L"Capture Source"); // add web cam to graph as source
IEnumPins* pins = 0; hr = cam?cam->EnumPins(&pins):0; // we need output pin to autogenerate rest of the graph
IPin* pin = 0; hr = pins?pins->Next(1,&pin, 0):0; // via graph->Render
hr = graph->Render(pin); // graph builder now builds whole filter chain including MJPG decompression on some webcams
IEnumFilters* fil = 0; hr = graph->EnumFilters(&fil); // from all newly added filters
IBaseFilter* rnd = 0; hr = fil->Next(1,&rnd,0); // we find last one (renderer)
hr = rnd->EnumPins(&pins); // because data we are intersted in are pumped to renderers input pin
hr = pins->Next(1,&pin, 0); // via Receive member of IMemInputPin interface
IMemInputPin* mem = 0; hr = pin->QueryInterface(IID_IMemInputPin,(void**)&mem);
DsHook(mem,6,Receive); // so we redirect it to our own proc to grab image data
hr = ctrl->Run();
while ( GetMessage( &msg, 0, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
};
Bonus points if you can tell me how get this thing not to render a window but still get me access to the image data.
That's really ugly. Please don't do that. Insert a pass-through filter like the sample grabber instead (as I replied to your other post on the same topic). Connecting the sample grabber to the null renderer gets you the bits in a clean, safe way without rendering the image.
To get the stride, you need to get the media type, either through ISampleGrabber or IPin::ConnectionMediaType. The format block will be either a VIDEOINFOHEADER or a VIDEOINFOHEADER2 (check the format GUID). The bitmapinfo header biWidth and biHeight defines the bitmap dimensions (and hence stride). If the RECT is not empty, then that defines the relevant image area within the bitmap.
I'm going to have to wash my hands now after touching this post.
I am sorry for you. When the interface was created there were probably not the best programmer to it.
// Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
BITMAPINFOHEADER bmpInfo; // current bitmap header info
int stride;
HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ;
HRESULT __stdcall Receive ( void* inst, IMediaSample *smp )
{
BYTE* buf; smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
HRESULT ret = Receive_ ( inst, smp );
AM_MEDIA_TYPE* info;
HRESULT hr = smp->GetMediaType(&info);
if ( hr != S_OK )
{ //TODO: error }
else
{
if ( type->formattype == FORMAT_VideoInfo )
{
const VIDEOINFOHEADER * vi = reinterpret_cast<VIDEOINFOHEADER*>( type->pbFormat );
const BITMAPINFOHEADER & bmiHeader = vi->bmiHeader;
//! now the bmiHeader.biWidth contains the data stride
stride = bmiHeader.biWidth;
bmpInfo = bmiHeader;
int width = ( vi->rcTarget.right - vi->rcTarget.left );
//! replace the data stride be the actual width
if ( width != 0 )
bmpInfo.biWidth = width;
}
else
{ // unsupported format }
}
DeleteMediaType( info );
return ret;
}
Here's how to add the Null Renderer that suppresses the rendering window. Add directly after creating the IGraphBuilder*
//create null renderer and add null renderer to graph
IBaseFilter *m_pNULLRenderer; hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&m_pNULLRenderer);
hr = graph->AddFilter(m_pNULLRenderer, L"Null Renderer");
That dshook hack is the only elegant directshow code of which I am aware.
In my experience, the DirectShow API is a convoluted nightmare, requiring hundreds of lines of code to do even the simplest operation, and adapting a whole programming paradigm in order to access your web camera. So if this code does the job for you, as it did for me, use it and enjoy fewer lines of code to maintain.