C++ How to display Imebra image (char buffer) as an image? - c++

I use an Imebra library to read DICOM files. I need to display DICOM image. Imebra library gives me an image in buffer (I follow this documentation):
load file
std::unique_ptr<DataSet> MyDataSet(CodecFactory::load("IM1"));
retrieving an image
Image* image(MyDataSet->getImageApplyModalityTransform(0));
int iWidth = image->getWidth();
int iHeight = image->getHeight();
TransformsChain chain;
if (ColorTransformsFactory::isMonochrome(image->getColorSpace()))
{
VOILUT voilutTransform;
vois_t vois = MyDataSet->getVOIs();
list<LUT*> luts;
for (size_t scanLUTs(0); ; ++scanLUTs)
{
try { luts.push_back(MyDataSet->getLUT(TagId(tagId_t::VOILUTSequence_0028_3010), scanLUTs)); }
catch (const MissingDataElementError&) { break; }
}
if (!vois.empty()) voilutTransform.setCenterWidth(vois[0].center, vois[0].width);
else if (!luts.empty()) voilutTransform.setLUT(*(luts.front()));
else voilutTransform.applyOptimalVOI(*image, 0, 0, iWidth, iHeight);
DrawBitmap draw(chain);
size_t requestedBufSize = draw.getBitmap(*image, drawBitmapType_t::drawBitmapRGBA, 4, 0, 0);
string buffer(requestedBufSize, char(0));
draw.getBitmap(*image, drawBitmapType_t::drawBitmapRGBA, 4, &buffer.at(0), requestedBufSize);
So I got a buffer with bitmap inside it. (following each word of documentation).
Then I get bitmap from this buffer:
hBitmap = CreateBitmap(iWidth, iHeight, 1, 24, buffer);
Now I'm trying to dispay it in MFC PictureControl (on dialog window):
void CImebraDlg::OnPaint()
{
CPaintDC dc(this);
CDC memdc;
BITMAP b;
//m_DicomImage - CStatic variable of PictureControl
m_DicomImage.GetClientRect(&rect);
::GetObject(theApp.hBitmap, sizeof(BITMAP), &b);
memdc.CreateCompatibleDC(&dc);
memdc.SelectObject(&bmp);
dc.StretchBlt(0, 0, rect.Width(), rect.Height(), &memdc,
0, 0, b.bmWidth, b.bmHeight, SRCCOPY);
dc.MoveTo(0, 0);
}
And nothig happens, no image in PictureControl.
Trying like this:
void CImebraDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetImageToRictureControl(theApp.hBitmap);
}
void CImebraDlg::SetImageToRictureControl(HBITMAP hbmp)
{
m_DicomImage.SetBitmap(hbmp);
UpdateData(FALSE);
}
the same result...
BITMAP structure you can see on the screenshot.
bmBits is NULL.. Is this the reason of not displaying dicom image?
How to display a DICOM image right with imebra lib?

Related

MFC Printing bitmap only prints in black

I have the following method to print a bitmap which did work perfectly but now it prints the area of the bitmap all in black. I've tested my test app which was compiled on my PC on another PC and it prints the bitmap perfectly. I've debugged it and it is opening the bitmap file because its reading the correct dimensions. I'm at a loss to see what has happen, Any advice would be greatly appreciated. Thanks in advance.
void CTestAppPrintDlg::OnBnClickedButton1()
{
CString path;
path = "Test1.bmp";
PrintBitmap(path);
}
void CTestAppPrintDlg::PrintBitmap(LPCTSTR filename) {
CPrintDialog printDlg(FALSE);
printDlg.GetDefaults();
return;
CDC dc;
if (!dc.Attach(printDlg.GetPrinterDC())) {
AfxMessageBox(_T("No printer found!")); return;
}
dc.m_bPrinting = TRUE;
DOCINFO di;
// Initialise print document details
::ZeroMemory(&di, sizeof(DOCINFO));
di.cbSize = sizeof(DOCINFO);
di.lpszDocName = filename;
BOOL bPrintingOK = dc.StartDoc(&di); // Begin a new print job
// Get the printing extents
// and store in the m_rectDraw field of a
// CPrintInfo object
CPrintInfo Info;
Info.SetMaxPage(1); // just one page
int maxw = dc.GetDeviceCaps(HORZRES);
int maxh = dc.GetDeviceCaps(VERTRES);
Info.m_rectDraw.SetRect(0, 0, maxw, maxh);
for (UINT page = Info.GetMinPage(); page <=
Info.GetMaxPage() && bPrintingOK; page++) {
dc.StartPage(); // begin new page
Info.m_nCurPage = page;
CBitmap bitmap;
// LoadImage does the trick here, it creates a DIB section
// You can also use a resource here
// by using MAKEINTRESOURCE() ... etc.
if (!bitmap.Attach(::LoadImage(
::GetModuleHandle(NULL), filename, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE))) {
AfxMessageBox(_T("Error loading bitmap!")); return;
}
BITMAP bm;
bitmap.GetBitmap(&bm);
int w = bm.bmWidth;
int h = bm.bmHeight;
// create memory device context
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap *pBmp = memDC.SelectObject(&bitmap);
memDC.SetMapMode(dc.GetMapMode());
dc.SetStretchBltMode(HALFTONE);
// now stretchblt to maximum width on page
dc.StretchBlt(0, 0, w, h, &memDC, 0, 0, w, h, SRCCOPY);
// clean up
memDC.SelectObject(pBmp);
bPrintingOK = (dc.EndPage() > 0); // end page
}
if (bPrintingOK)
dc.EndDoc(); // end a print job
else dc.AbortDoc(); // abort job.
}
Thanks for the person who gave me a negative rating. This was very helpful!
I've found that it was nothing to do with my code and the cause was the Windows Update KB5000802. I uninstalled this update and it now works.

Correctly displaying s 32 bit transparent PNG file in a DC

This is my method for loading a transparent PNG file into a buffer:
/* static */ void CRibbonButton::LoadImageFromRelativeFilespec(HGLOBAL& rhDIB, bool bLarge,
const CString& rstrImageRelFilespec, UINT32& ruDIBW, int& ruDIBH)
{
USES_CONVERSION;
using namespace RibbonBar ;
// Clear any existing image away.
if (rhDIB != NULL)
::GlobalFree(rhDIB);
// Build the correct filespec.
CString strThisEXE = _T("");
::GetModuleFileName(AfxGetInstanceHandle(),
strThisEXE.GetBuffer(_MAX_PATH + 1),_MAX_PATH);
strThisEXE.ReleaseBuffer();
LPCTSTR lpszPath = (LPCTSTR)strThisEXE ;
LPTSTR lpszFilename = ::PathFindFileName(lpszPath);
CString strPath = strThisEXE.Left( (int)(lpszFilename - lpszPath) );
CString strFilespec = strPath ;
::PathAppend(strFilespec.GetBuffer(_MAX_PATH + 1), rstrImageRelFilespec);
strFilespec.ReleaseBuffer();
HISSRC hSrc = is6_OpenFileSource(CT2A((LPCTSTR)strFilespec));
if (hSrc)
{
// read it
UINT32 w, h;
rhDIB = is6_ReadImage(hSrc, &w, &h, 2, 0); // the "2" = load directly to DIB, in the lowest bit depth possible.
if (rhDIB)
{
// get the dimensions
is6_DIBWidth((BITMAPINFOHEADER *)rhDIB, &ruDIBW);
is6_DIBHeight((BITMAPINFOHEADER *)rhDIB, &ruDIBH);
UINT32 bc;
is6_DIBBitCount((BITMAPINFOHEADER *)rhDIB, &bc);
is6_ClearJPGInputMarkers();
}
else
{
AfxMessageBox(_T("Can't read that image"));
}
is6_CloseSource(hSrc);
}
}
And this is the rendering code:
/* virtual */ void CRibbonButton::PaintData(CDC& rDC)
{
CDC dcMem ;
dcMem.CreateCompatibleDC(NULL); // Screen.
const CRect& rrctImage = GetImageBounds();
if (m_hDIB)
{
// draw to a memory DC
CDC memDC;
if (memDC.CreateCompatibleDC(&rDC))
{
CBitmap bmp;
if (bmp.CreateCompatibleBitmap(&rDC, rrctImage.Width(), rrctImage.Height()))
{
CBitmap *ob = memDC.SelectObject(&bmp);
if (ob)
{
// dark red background
memDC.FillSolidRect(CRect(rrctImage.left, rrctImage.top, rrctImage.Width(), rrctImage.Height()), RibbonBar::kBackColour);
// stretchDrawDIB is typically the fastest way to draw an image from ImgSource.
BOOL ok = is6_StretchDrawDIB(memDC.m_hDC, (BITMAPINFOHEADER *)m_hDIB, 0, 0, m_uDIBW, m_uDIBH);
if (!ok)
{
memDC.SetBkMode(TRANSPARENT);
memDC.SetTextColor(RGB(255, 255, 255));
memDC.TextOut(rrctImage.left, rrctImage.top, _T("X"));
}
// copy this to the window
rDC.BitBlt(rrctImage.left, rrctImage.top, rrctImage.Width(), rrctImage.Height(), &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(ob);
}
}
}
}
dcMem.DeleteDC();
}
It is not drawing the transparent PNG file correctly. I always end up with a black background.
I am using the ISSource libraries for rendering. But the company is now out of business. I am using version 6 library.
Update
Based on the answer I am now loading and rendering the image like this:
CRect rct;
CImage img;
img.Load(_T("d:\\Publishers.png"));
rct.SetRect(rrctImage.left, rrctImage.top, rrctImage.left + img.GetWidth(), rrctImage.top + img.GetHeight());
img.TransparentBlt(rDC.GetSafeHdc(), rct, RGB(255,255,255));
But why do I still get black for where the transparency was set?
If I don't pass RGB(255,255,255) as the last parameter
and use the default I get an exception.
Update
According to the documentation for TransparentBit:
TransparentBlt is supported for source bitmaps of 4 bits per pixel and 8 bits per pixel. Use CImage::AlphaBlend to specify 32 bits-per-pixel bitmaps with transparency.
So, I have stopped using:
img.TransparentBlt(rDC.GetSafeHdc(), rct);
Now I am using:
img.AlphaBlend(rDC.GetSafeHdc(), rct.left, rct.top, rct.Width(), rct.Height(), rct.left, rct.top, rct.Width(), rct.Height(), 0xff, AC_SRC_OVER);
I don't see anything. I confirm the coordinates are right by doing:
CBrush br;
br.CreateStockObject(BLACK_BRUSH);
rDC.FrameRect(rct, &br);
Why do I not see anything?
This is much to complicate. There are existing methods in CImage.
Check out CImage::AlphaBlend or CImage::TransparentBlt.
AlphaBlend: The Dst fields are the coordinates in your DC. the Src values are inside your picture. Usually they start with 0,0 and have the width and height as values. Is xSrc/ySrc are not 0 you have an offset in the source.

How to convert cv::Mat to MFC CBitmap

how can I convert an OpenCV image to an Microsoft Foundation Classes (MFC) CBitmap object?
I tried the following, which failed,
cv::Mat tmp;
(Load opencv image ...)
cv::Size size = tmp.size();
CBitmap bitmap;
// 3 colors (RGB), 24bits (8bits*3channels)
if (!bitmap.CreateBitmap(128, 128, 1, 24, (void *)tmp.data)) {
TRACE0("Failed to create bitmap for image display\n");
return;
}
this results in a black image..
This post shares the method of converting an IplImage to CBitmap:
CBitmap* IplImageToCBitmap(IplImage* img)
{
CDC dc;
CDC memDC;
if (!dc.CreateDC("DISPLAY", NULL, NULL, NULL))
return NULL;
if (!memDC.CreateCompatibleDC(&dc))
return NULL;
CBitmap* bmp = new CBitmap();
CBitmap* pOldBitmap;
bmp->CreateCompatibleBitmap(&dc, img->width, img->height);
pOldBitmap = memDC.SelectObject(bmp);
CvvImage cvImage; // you will need OpenCV_2.2.0- to use CvvImage
cvImage.CopyOf(img);
cvImage.Show(memDC.m_hDC, 0, 0, img->width, img->height, 0, 0);
cvImage.Destroy();
memDC.SelectObject(pOldBitmap);
memDC.DeleteDC();
dc.DeleteDC();
return bmp;
}
Based on this, you can achieve your goal by calling it as follows:
cv::Mat aMat;
CBitmap *aCBitmap = IplImageToCBitmap((IplImage*) &aMat);

OpenCV 2.4 : Displaying a cv::Mat in MFC

I am updating an existing code base from IplImage* to the newer cv::Mat. I was wondering how to display my cv::Mat object to MFC. The current solution we are using is based on the old CvvImage class:
void DrawPicToHDC(IplImage *img, UINT ID, bool bOnPaint)
{
CDC *pDC = GetDlgItem(ID)->GetDC();
HDC hDC= pDC->GetSafeHdc();
CRect rect;
GetDlgItem(ID)->GetClientRect(&rect);
CvvImage cimg;
cimg.CopyOf( img );
cimg.DrawToHDC( hDC, &rect );
ReleaseDC( pDC );
}
I came across this thread but unfortunately the answer provided doesn't meet my needs because the answers still require you to convert the Mat to an IplImage* before displaying.
Is there any way to do this using cv::Mat only? Any help is much appreciated.
UPDATE:
I adapted the above function to using cv::Mat with the help from Kornel's answer. I now do not need to include the CvvImage class:
void DrawPicToHDC(cv::Mat cvImg, UINT ID, bool bOnPaint)
{
// Get the HDC handle information from the ID passed
CDC *pDC = GetDlgItem(ID)->GetDC();
HDC hDCDst = pDC->GetSafeHdc();
CRect rect;
GetDlgItem(ID)->GetClientRect(&rect);
cv::Size winSize(rect.right, rect.bottom);
// Resize the source to the size of the destination image if necessary
cv::Mat cvImgTmp(winSize, CV_8UC3);
if (cvImg.size() != winSize)
{
cv::resize(cvImg, cvImgTmp, winSize);
}
else
{
cvImgTmp = cvImg;
}
// Rotate the image
cv::flip(cvImgTmp,cvImgTmp,0);
// Initialize the BITMAPINFO structure
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = winSize.width;
bitInfo.bmiHeader.biHeight = winSize.height;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
// Add header and OPENCV image's data to the HDC
StretchDIBits(hDCDst, 0, 0,
winSize.width, winSize.height, 0, 0,
winSize.width, winSize.height,
cvImgTmp.data, &bitInfo, DIB_RGB_COLORS, SRCCOPY);
ReleaseDC( pDC );
}
Suppose we have an OpenCV image cv::Mat cvImg which one should be converted to CImage* mfcImg.
Of course the MFC image should be displayed on an MFC window i.e. in CStatic winImg
So the following transformations should be performed in order to display an OpenCV image in an MFC window:
cv::Mat -> CImage -> CStatic
Define MFC window size:
RECT r;
winImg.GetClientRect(&r);
cv::Size winSize(r.right, r.bottom);
The size of cvImg is not always the same as an MFC window’s:
cv::Mat cvImgTmp(winSize, CV_8UC3);
if (cvImg.size() != winSize)
{
cv::resize(cvImg, cvImgTmp, winSize);
}
else
{
cvImgTmp = cvImg.clone();
}
Rotate the image:
cv::flip(cvImgTmp, cvImgTmp, 0);
Create an MFC image:
if (mfcImg)
{
mfcImg->ReleaseDC();
delete mfcImg; mfcImg = nullptr;
}
mfcImg = new CImage();
mfcImg->Create(winSize.width, winSize.height, 24);
For mfcImg you need a header. Create it by using BITMAPINFO structure
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = winSize.width;
bitInfo.bmiHeader.biHeight = winSize.height;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
Add header and OpenCV image’s data to mfcImg
StretchDIBits(mfcImg->GetDC(), 0, 0,
winSize.width, winSize.height, 0, 0,
winSize.width, winSize.height,
cvImgTmp.data, &bitInfo, DIB_RGB_COLORS, SRCCOPY
);
Display mfcImg in MFC window
mfcImg->BitBlt(::GetDC(winImg.m_hWnd), 0, 0);
Release mfcImg, if you will not use it:
if (mfcImg)
{
mfcImg->ReleaseDC();
delete mfcImg; mfcImg = nullptr;
}

Load BMP from file by using MFC

I try to load a bmp to my MFC Picture Control.
void CMFCAppDlg::OnBnClickedButtonload()
{
CFileDialog dlg(TRUE);
int result=dlg.DoModal();
if(result==IDOK)
{
MyBmpFile::Instance() -> setPath (dlg.GetPathName());
UpdateData(FALSE);
}
HANDLE hBitmap = LoadImage(0, MyBmpFile::Instance() -> getPath(), IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
CBitmap m_bitmap;
m_bitmap.Attach((HBITMAP)hBitmap);
CDC dc, *pDC;
BITMAP bmp;
m_bitmap.LoadBitmapW(IDB_BITMAP);
m_bitmap.GetBitmap(&bmp);
pDC = this->GetDC();
dc.CreateCompatibleDC(pDC);
dc.SelectObject(m_bitmap);
pDC->BitBlt(200, 200, bmp.bmWidth, bmp.bmHeight, &dc,0 , 0, SRCCOPY);
m_bitmap.DeleteObject();
m_bitmap.Detach();
}
This code returns me an error after I select an item in dialog box. Problem is with LoadImage() it returns NULL. But actually I dont know what im doing wrong with that.
Ok, I used CImage to draw this bmp, anyway i did not solve the problem with LoadImage(). I try to make it in static way like: L"D:\\e.bmp" or _T("D:\\e.bmp") but even there problem is the same as before.
void CMFCAppDlg::OnBnClickedButtonload()
{
CFileDialog dlg(TRUE);
int result=dlg.DoModal();
if(result==IDOK)
{
MyBmpFile::Instance() -> setPath (dlg.GetPathName());
UpdateData(FALSE);
}
CImage image;
image.Load( MyBmpFile::Instance() ->getPath() );
CDC dc, *pDC;
pDC = this->GetDC();
dc.CreateCompatibleDC(pDC);
image.Draw(pDC -> GetSafeHdc(),0,0);
}
Following may be of help:
INSTANCE hInst = AfxGetInstanceHandle();
HBITMAP hBmp = (HBITMAP)LoadImage(hInst, L"path\to\file.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
Couple of things to check:
Are you using 1-byte or 2-byte characters. Use the L macro for the latter.
Is the path to your file correctly specified (e.g. could be relative to where the program happens to run from)
Can you load the file manually as a bitmap as in the example below?
Code to load a file manually as a bitmap:
CFile file;
if (file.Open(L"C:\\Tmp\\Example.bmp", CFile::modeRead))
{
// Read file header
BITMAPFILEHEADER bmfHeader;
if (file.Read((LPSTR) &bmfHeader, sizeof(bmfHeader)) == sizeof(bmfHeader))
{
// File type should be 'BM'
if (bmfHeader.bfType == ((WORD)('M' << 8)| 'B'))
{
BITMAPINFOHEADER bmiHeader;
if (file.Read((LPSTR) &bmiHeader, sizeof(bmiHeader)) == sizeof(bmiHeader))
{
int width = bmiHeader.biWidth;
int height = bmiHeader.biHeight;
}
}
}
file.Close();
}