twitter

Bonafide Ideas Rss

Featured Posts

Microsoft Visual C++ MVP Award for 2013 G’day Community, Firstly, I’d like to wish you all a happy new year 2013, full of health, blessings & success Secondly, I’m happy to announce that Microsoft has awarded me with an MVP (9th consecutive year) in the Visual C++ category, therefore I’d like to thank God, my daughter, my wife (for all of her patience & understanding),...

Read more

Analysing assemblies and finding out possible leaks... Today’s post is about… A command line utility I wrote a few days back for analysing and finding out possible leaks by not disposing of objects properly. Before we get into the nuts and bolts of the utility, please find attached two files which are: MSIL Instruction Set Specification (Required to do any MSIL manipulation) Utility’s...

Read more

CyberNanny: Remote Access via Distributed Components This article is about an application called CyberNanny, which I recently wrote to allow me to remotely see my baby daughter Miranda at home from anywhere at any time. It’s written in Visual C++ (MFC) and it comprises different technologies such as Kinect and its SDK, Windows Azure, Web services and Office automation via Outlook. The project...

Read more

DDD Sydney - Slide deck Hi community, Please feel free to grab the slides I used at DDD Sydney from here Regards,   Angel

Read more

Developer… Developer… Developer Sydney! Hi Community, I would like to invite you all to “Developer… Developer… Developer Sydney”. This event will be held at UTS on Saturday 30th June 2012. I’ll be presenting “Building Windows 8 applications natively with Visual C++”. I look forward to seeing you there. Best Regards, Angel

Read more

Taking pictures with Kinect & calling GDI+ to reduce the file size

Category : C++, COM, GDI+, Kinect, Visual C++, Win32

Hi Community,

As mentioned in my previous post, I’ve been putting together an application which comprises technologies like Kinect, Windows Azure & Visual C++. The application name is “CyberNanny” and I wrote it for my beautiful newborn daughter Smile. Actually, I’ve submitted today an article to MSDN magazine about the aforementioned solution.

Today’s post is about how we can take pictures with the Kinect sensor and reduce the size of the resulting image file (for emailing purposes).

Let’s assume that we have a smart pointer of type INuiSensor. INuiSensor is the COM representation of the physical sensor. We also have another smart pointer that holds a reference to a custom class called DrawDevice that performs the drawing via Direct2D and  ID2D1Factory

CComPtr<INuiSensor> m_pSensor;

 

std::shared_ptr<DrawDevice> m_pDrawColor;

 


 

void  Nui_Core::TakePicture(std::shared_ptr<BYTE>& imageBytes, int& bytesCount) {

 

    byte *bytes;

 

    NUI_IMAGE_FRAME imageFrame;

 

    NUI_LOCKED_RECT LockedRect;

 


 

    if (SUCCEEDED(m_pSensor->NuiImageStreamGetNextFrame(m_hVideoStream, m_millisecondsToWait, &imageFrame))) {

 

        auto pTexture = imageFrame.pFrameTexture;

 

        pTexture->LockRect(0, &LockedRect, NULL, 0);

 


 

        if (LockedRect.Pitch != 0) {

 

            bytes = static_cast<BYTE *>(LockedRect.pBits);

 

            m_pDrawColor->Draw(bytes, LockedRect.size);

 

        }

 


 

        pTexture->UnlockRect(0);

 

        imageBytes.reset(new BYTE[LockedRect.size]);

 

        memcpy(imageBytes.get(), bytes, LockedRect.size);

 

        bytesCount = LockedRect.size;

 

        m_pSensor->NuiImageStreamReleaseFrame(m_hVideoStream, &imageFrame );

 

    }

 

}

 

Please note that we deal with a pointer to byte (array of bytes) and it’s not a safe practice to return a pointer because the memory address might change and our pointer might point to something else, therefore we pass a smart pointer by reference to store the bytes in it.

The code to handcraft our image (picture)  which is a BMP is shown below

std::wstring ImageFile::SerializeImage(std::shared_ptr<BYTE>& bytes, int byteCount) {

 

    RPC_WSTR guidAsStr;

 

    WCHAR tempPath[MAX_PATH];

 

    std::wstring retval, tempFile;

 

    std::unique_ptr<GUID> guid(new GUID);

 

    CoCreateGuid(guid.get());

 

    UuidToString(guid.get(), &guidAsStr);

 

    auto str = std::wstring((LPTSTR) guidAsStr);

 

    GetTempPath(MAX_PATH, tempPath);

 

    tempFile = std::wstring(tempPath).append(L"photo_").append(str).append(L".bmp");

 

    auto infoHeader = GetBitmapInfoHeader();

 

    auto fileHeader = GetBitmapFileHeader(infoHeader);

 


 

    if (SerializeImageHelper(infoHeader, fileHeader, tempFile, bytes, byteCount)) {

 

        if (RotateImage(tempFile, retval))

 

            m_savedImagePath = retval;

 

    } else retval = L"";

 


 

    return retval;

 

}

 


 

BITMAPINFOHEADER ImageFile::GetBitmapInfoHeader() {

 

    BITMAPINFOHEADER retval = {0};

 


 

    retval.biSize = sizeof(BITMAPINFOHEADER);

 

    retval.biBitCount = 32;

 

    retval.biPlanes = 1;

 

    retval.biCompression = BI_RGB;

 

    retval.biWidth = 640;

 

    retval.biHeight = 480;

 

    retval.biSizeImage = ((((retval.biWidth * retval.biBitCount) + 31) & ~31) >> 3) * retval.biHeight;

 


 

    return retval;

 

}

 


 

BITMAPFILEHEADER ImageFile::GetBitmapFileHeader(const BITMAPINFOHEADER& infoHeader) {

 

    BITMAPFILEHEADER retval = {0};

 


 

    auto nBitsOffset = sizeof(BITMAPFILEHEADER) + infoHeader.biSize;

 

    auto lImageSize = infoHeader.biSizeImage;

 

    auto lFileSize = nBitsOffset + lImageSize;

 

    retval.bfType = 'B'+('M'<<8);

 

    retval.bfOffBits = nBitsOffset;

 

    retval.bfSize = lFileSize;

 


 

    return retval;

 

}

 


 

bool ImageFile::SerializeImageHelper(const BITMAPINFOHEADER& infoHeader, const BITMAPFILEHEADER& fileHeader,

 

                                     std::wstring& filePath, std::shared_ptr<BYTE>& bytes, int byteCount) {

 

    auto retval = false;

 

    auto hFile = CreateFile(filePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

 


 

    if (hFile !=  INVALID_HANDLE_VALUE) {

 

        auto newFile = CFile(hFile);

 

        newFile.Write(&fileHeader, sizeof(BITMAPFILEHEADER));

 

        newFile.Write(&infoHeader, sizeof(BITMAPINFOHEADER));

 

        newFile.Write(bytes.get(), byteCount);

 

        newFile.Close();

 

        retval = true;

 

    }

 


 

    return retval;

 

}

 

In order to save our BMP, we must specify two things before serializing the bytes:

Another detail to take into account is, the resulting bitmap is approximately 1.7 MB in size and it’s upside down – This is not very convenient for emailing purposes, so we have to rotate the image 180 degrees and save it with a different format (Encode it), the snippet below shows how we can do this

bool ImageFile::RotateImage(const std::wstring& filePath, std::wstring& newFile) {

 

    CLSID jpgClsid;

 

    newFile = filePath;

 

    auto retval = false;

 

    ULONG_PTR gdiplusToken;

 

    GdiplusStartupInput gdiplusStartupInput;

 

    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

 


 

    if ((retval = GetEncoderClsid(L"image/jpeg", &jpgClsid))) {

 

        auto fileToRotate = new Image(filePath.c_str(), FALSE);

 

        newFile.replace(newFile.find(L"bmp"), 3, L"jpg");

 

        fileToRotate->RotateFlip(Rotate180FlipNone);

 

        fileToRotate->Save(newFile.c_str(), &jpgClsid, NULL);

 

        delete fileToRotate;

 

    }

 


 

    ::DeleteFile(filePath.c_str());

 


 

    GdiplusShutdown(gdiplusToken);

 


 

    return retval;

 

}

 


 

bool ImageFile::GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {

 

    UINT  num = 0;

 

    UINT  size = 0;

 

    auto retval = false;

 


 

    ImageCodecInfo* pImageCodecInfo = NULL;

 


 

    if ((GetImageEncodersSize(&num, &size)) == Status::Ok && (pImageCodecInfo = (ImageCodecInfo*)(malloc(size))) != NULL) {

 

        if (GetImageEncoders(num, size, pImageCodecInfo) == Status::Ok) {

 

            for(auto index = 0; index < num; ++index)  {

 

                if(wcscmp(pImageCodecInfo[index].MimeType, format) == 0 ) {

 

                    *pClsid = pImageCodecInfo[index].Clsid;

 

                    free(pImageCodecInfo);

 

                    pImageCodecInfo = NULL;

 

                    retval = true;

 

                    break;

 

                }

 

            }

 

        }

 

    }

 


 


 

    if (!pImageCodecInfo)

 

        free(pImageCodecInfo);

 


 

    return retval;

 

}

 

 

As you can see, we reduce the file size by saving the image as a JPEG by calling the Save method of the Image class (from 1.7 MB the file size was reduced to 32 KB) and we rotate the image by calling the RotateFlip method of the same class. This functionality is provided by GDI+. It’s important to note that GDI+ needs to be initialized and then shutdown once we’ve finished using it.

Best Regards,

Angel

Post a comment