PDA

View Full Version : show image



ruicosta
May 24, 2005, 20:22:31
Hello,
I have implemented the following steps:
- .....
- create the memory buffer
- start live video
- snap live video
- acess to the image data in buffer and change some pixels

How can I show the image that was in the buffer in my own dialog window ?


Best Regards,
Rui Costa

Stefan Geissler
May 25, 2005, 10:00:18
Hello Rui,

have a look on the onPaint method in Childview.cpp of the demoapp. It uses displays a memory buffer in window using simple GDI functions.

tdugard
May 25, 2005, 16:45:44
I have the same problem as ruicosta (but i'm only be able the display the image of the camera, i can't modify the pixel of the image it crash), and i want to know if someone could send me a entire sample program (very simple plz) because my knowledge in C++ is very basic.

Thanks a lot.

Tony DUGARD

Stefan Geissler
May 25, 2005, 17:01:06
Imageprocessing is done within 5 steps:

1. Setup the grabber, that means open the video capture device, select the video format.
2. Then create the memory buffer, where the images are to be saved in.
3. Start the live video and snap an image
4. Stop the live video.
5. Access the image data.

The source code would look like this (it is copied from the „Callback“ sample:


if( !DShowLib::InitLibrary( 0 ) )
{
fprintf( stderr, "The library could not be initialized ");
fprintf( stderr, "(invalid license key?).\n");
exit( 1 );
}

Grabber *grabber = new DshowLib::Grabber();

// This is coded in the „common\setupDevice.cpp“ and sets the video norm
// and video format.
if ( !setupDevice( grabber ) )
{
delete grabber;
exit( 1 );
}

// Set the colorformat for the images in memory.
grabber->setSinkType( FrameGrabberSink(
FrameGrabberSink::tFrameGrabberMode::eSNAP, DshowLib::eRGB24) );


// Now create the memory buffer for the images.
Grabber::tMemBufferCollectionPtr pMemBuffColl = grabber->
newMemBufferCollection( 2 );
grabber->setActiveMemBufferCollection( pMemBuffColl );

// Snap an Image
grabber->startLive( true ); // Start the grabber.
grabber->snapImages( 1 ); // Grab NUM_BUFFERS images.
grabber->stopLive();

// Access the image data:
BYTE *pData = m_Grabber.getActiveMemBuffer()->getPtr();


Now you can access the image data, that are pointed to by pData. It is just an unformated stream of bytes. Regarding to the color format in memory, three bytes will specify one pixel. The colors are save as blue, green red.

tdugard
May 25, 2005, 17:37:38
OK thanks but my probleme is in the utilisation of MFC. In which classes do i write the processus ?

Stefan Geissler
May 26, 2005, 11:52:57
Hello,

I have created a template based on the Demoapp for image processing. It does continous image processing on a RGB 24 sink and displays the result in the application's child window. The infrastructure for device selection and image settings is implemented. You can use this program template as base for your image processings.

You only need to alter the class „Clistener“ in „listener.cpp“. In this class is a method „DoImageProcessing“, that receives the currently provided memorybuffer.


void CListener::DoImageProcessing( smart_ptr<MemBuffer> pBuffer)
{
// Get the bitmap info header from the membuffer. This contains the bits per pixel,
// width and height.
smart_ptr<BITMAPINFOHEADER> pInf = pBuffer->getBitmapInfoHeader();

// Now retrieve a pointer to the image. For organization of the image data, please
// refer to:
// http://www.imagingcontrol.com/ic/docs/html/class/Pixelformat.htm

BYTE* pImageData = pBuffer->getPtr();

// Calculate the size of the image.
int iImageSize = pInf->biWidth * pInf->biHeight * pInf->biBitCount / 8 ;

// Now loop through the data and change every byte.
for( int i = 0; i < iImageSize; i++)
{
pImageData[i] = 255 - pImageData[i];
}
}


This sample code changes the bytes of the image directly in the memory buffer. The image is altered.

After „DoImageProcessing“ has finished, the result is displayed in the method „DrawBuffer“:


void CListener::DrawBuffer( smart_ptr<MemBuffer> pBuffer)
{
if( m_pDrawCWnd != NULL)
{
if( pBuffer != 0 )
{
CDC *pDC = m_pDrawCWnd->GetDC();

smart_ptr<BITMAPINFOHEADER> pInf = pBuffer->getBitmapInfoHeader();



int nLines = StretchDIBits( pDC->GetSafeHdc(),// Handle to the device
0,
0,
pInf->biWidth, // Dest rectangle width
pInf->biHeight, // Dest rectangle height
0, // X-coordinate of lower-left corner of the source rect
0, // Y-coordinate of lower-left corner of the source rect
pInf->biWidth, // Source rectangle width
pInf->biHeight, // Number of scan lines
pBuffer->getPtr(), // Modified address of array with DIB bits
reinterpret_cast<LPBITMAPINFO>( &*pInf ), // Address of structure with bitmap info
DIB_RGB_COLORS, // RGB or palette indices
SRCCOPY
);
m_pDrawCWnd->ReleaseDC(pDC);
}
}
}


The m_pDrawCWnd handle has been passed in the „CMainFrame“ class to the Clistener object.
For organization of the image data, please refer to:
http://www.imagingcontrol.com/ic/docs/html/class/Pixelformat.htm
Please download the attached sample and unzip the content to your IC Imaging Control samples\vc6 directory.

tdugard
May 26, 2005, 13:54:13
Thanks a lot i'll try that. ;)

tdugard
May 26, 2005, 15:50:29
It works perfectly thanks a lot, now I have to copy in memory a Reference image to make traitements refering to it, how could i do that ? Can you explain me the procedure...

So I made one button on the frame and when i click to it I want to snap an image in a separate buffer in memory but I don't understand how works the active MemBuffer

Stefan Geissler
May 27, 2005, 11:15:07
Tony

I will reinterpret your assignment. You want to perform continuous image processing. Sometimes you want to copy an image to a special memory.

First of all you have to allocate the memory. This memory must have the same size as the image data in the frames. The type of the elements in this memory is BYTE. Start adding a new member to the CListener in the “private” section in “listener.h”:


private:
BYTE* m_pImageData;

In the constructor of CListener assign NULL to this pointer:


CListener::CListener()
{
m_pParent = NULL;
m_pDrawCWnd = NULL;
m_pImageData = NULL;
}

To avoid memory leaks, you should delete the image data memory in the destructor.


CListener::~CListener()
{
if( m_pImageData != NULL )
delete m_pImageData;
}

Now the CListener object must be informed, if it should copy an image to the m_pImageData memory. We will need a new attribute in the CListener class:


private:
BYTE* m_pImageData;
bool m_bSaveImage;

This new attribute is set to false in the constructor, we do not want to save the first image that comes in.


CListener::CListener()
{
m_pParent = NULL;
m_pDrawCWnd = NULL;
m_pImageData = NULL;
bSaveImage = false;
}

A public method is added, that will set bSaveImage to true. In the header insert in the CListener class:


public:
void SetSnapImage();

The implementation of this method in listener.cpp is


void CListener::SetSnapImage()
{
m_bSaveImage = true;
}

In your main program (Mainfram.cpp) you have added a button event handler. In this handler you have to call the SetSnapImage() of your CListener object:


Cmainframe::TheButtonHandler() // What ever name it has...
{
m_cListener.SetSnapImage();
}

Now the CListener knows, that is should save the next incoming frame to the image buffer. We have to alter the CListener::DoImageProcessing method. If m_bSaveImage is true, then we will copy the current frame in pBuffer into the memory, that m_pImageBuffer is pointing to. Well, the memory is not allocated yet, so we have to check this:


void CListener::DoImageProcessing( smart_ptr<MemBuffer> pBuffer)
{
smart_ptr<BITMAPINFOHEADER> pInf = pBuffer->getBitmapInfoHeader();
BYTE* pImageData = pBuffer->getPtr();
int iImageSize = pInf->biWidth * pInf->biHeight * pInf->biBitCount / 8 ;
if( m_bSaveImage == true )
{
if( m_pImageData == NULL ) // No memory allocated?
{
m_pImageData = new BYTE[ iImageSize];
}
memcpy( pImageData, m_pImageData, iImageSize); // Copy the image data.
m_bSaveImage = false;
}
}

If m_pImageBuffer is NULL, we have to allocate the memory. We only need the count of bytes, that are needed for the frame. After the memory is allocated, m_pImageBuffer is not NULL.
With memcpy() the bytes are copied from the frame into the memory buffer of m_pImageBuffer. You have your extra image saved now! Do not forget to set m_bSaveImage to false! If you forget it, every new frame will be copied into your memory buffer m_pImageBuffer.
As you can see, most work is done in the CListener class, only one method is to be exported and called from your frame work.

I hope, this little text answers your question.

tdugard
May 27, 2005, 12:06:59
This is exactly what i wanted, you are very nice i'll try that now

(Sorry if you don't understand what i said but i'm french and speak english in technicals words is quite hard)

tdugard
May 27, 2005, 12:45:14
And one last problem when i do one button in the toolbar i have to :

In the header mainFrm.h add an


afx_msg void myFunction();
afx_msg void OnUpdateMyFonction(CCmdUI* pCmdUI);

The first one is the fonction which is called when I click on the button and the second one is the fonction to enabled disable the button

In the mainFrm.cpp :


ON_UPDATE_COMMAND_UI(ID_MYBUTTON, OnUpdateMyFunction)[
ON_COMMAND(ID_MYBUTTON, MyFunction)

In the mainFrm.cpp here is the code of my function :


void CMainFrame::MyFunction()
{
m_cListener.SetSnapImage();
}

And in the ressourcewiew tab in VC++ 6.0 i have added a button which has ID_MYBUTTON for ID.

The function to enable disable the button works but the other no..

Stefan Geissler
May 27, 2005, 13:01:18
Tony,

if you work with VC 6, use the class wizard to insert the button handler. This is much more easier.

tdugard
May 27, 2005, 13:06:49
What do you call the class wizard ?

tdugard
May 27, 2005, 15:41:44
Ok now my button work the message is ON_BN_CLICKED instead of ON_COMMAND but my problem is the organisation of the pixel

if want to make a threshold :



// Now loop through the data and change every byte.
for( int i = 0; i < iImageSize; i++)
{
if(fabs(m_pImageData[i]-pImageData[i]) < 20)
{
pImageData[i] = 0;
}
else
{
pImageData[i] = 255;
}
}


I have already implemented tihs algorythm in JAVA but i don't understand how is represented ImageData and how to have pixel of it (i look the samples\vc6\Pixelformat sample program)

My goal is to have only on the image object in movement.

Stefan Geissler
May 27, 2005, 16:00:17
Hello,

The code you have written is ok, but it would work only, if your memory buffer format is Y8, that means 1 byte per pixel. If you have a RGB24 color format, then you have 3 bytes per pixel. See here: For organization of the image data, please refer to:
http://www.imagingcontrol.com/ic/docs/html/class/Pixelformat.htm
There is a very detailed documentation of available pixel formats. We spend a lot of work on this!

For RGB24 you would have to calculate the average of three bytes in your loop:


for( int i = 0; i < iImageSize; i+=3)
{
float p1, p2;
p1 = (m_pImageData[i] + m_pImageData[i+1] + m_pImageData[i+2])/3;
p2 = (pImageData[i] + pImageData[i+1] + pImageData[i+2])/3;

if(fabs(m_pImageData[i]-pImageData[i]) < 20)
{
pImageData[i] = 0;
pImageData[i+1] = 0;
pImageData[i+2] = 0;
}
else
{
pImageData[i] = 255;
pImageData[i+1] = 255;
pImageData[i+2] = 255;
}
}

tdugard
May 27, 2005, 16:11:29
My memory buffer is in Y8 (because i'm in RGB8bis grayscale), i have changed :

in MainFrm.cpp :



// Set the sinktype to 24 bit.
m_Grabber.setSinkType( DShowLib::FrameGrabberSink(
DShowLib::FrameGrabberSink::tFrameGrabberMode::eGR AB, DShowLib::eRGB24 ) );


To



// Set the sinktype to 8 bit.
m_Grabber.setSinkType( DShowLib::FrameGrabberSink(
DShowLib::FrameGrabberSink::tFrameGrabberMode::eGR AB, DShowLib::eY8 ) );


Two times in void CMainFrame::OnSettingsDevice() and in CMainFrame::LoadPreviouslyUsedDevice()

Is it wrong ?

tdugard
May 27, 2005, 16:19:02
I've try in RGB24 it works like in 8bit but its doesn't do what i want when the button is realised do I have to catch an afx message (maybe it does the snapshot every time the programs do the image processing)

Stefan Geissler
May 27, 2005, 17:39:52
If you work with Visual Studio 6, you can use the built in Classwizard, that makes inserts the button handler.

tdugard
May 28, 2005, 00:44:12
The bouton works but the algorythme is bugged i want to have only the body of a moving person

tdugard
May 30, 2005, 09:35:08
Hi,

To check if the image captured is good how can I display it in a another frame

EDIT : I've displayed my Reference Image and it's all gray

tdugard
May 30, 2005, 10:01:48
I've solve my problem it was the function memcpy which doesn't copy the buffer of the displayed image into the memory buffer so i do that :


for( int i = 0; i < iImageSize; i++)
{
m_pImageData[i] = pImageData[i];
}

Humm... finally i found the problem with memcpy



memcpy(m_pImageData, pImageData, iImageSize);

Instead of



memcpy(pImageData, m_pImageData, iImageSize);

Stefan Geissler
May 30, 2005, 10:25:54
Hello,

Sorry, it was my error. I exchanged target and source at memcpy().

tdugard
May 30, 2005, 11:22:08
No problem you were very helpul, now i want to unsderstand that line (in java Image are represented in matrix)


// Calculate the size of the image.
int iImageSize = pInf->biWidth * pInf->biHeight * pInf->biBitCount / 8 ;


Why do you divide by 8 ?

Stefan Geissler
May 30, 2005, 11:50:00
Hi,

that is simple: biBitCount says, the bits are counted. In RGB 24 you have 24 bits. These are 3 bytes in memory. 24/8 = 3. ( bits = 1 Byte. If i would not devide by 8, then the memory allocated would be 8 times to big.

tdugard
May 30, 2005, 12:02:34
Ok thx


Look at the attached image to tell me if i'm right about the representation of the image

Stefan Geissler
May 30, 2005, 13:33:11
Hi,

This is correct, except the image is stored upside down.

tdugard
May 30, 2005, 13:49:40
Ok if I understand Image is stored like that

Thanks for your patience

Stefan Geissler
May 30, 2005, 14:08:50
No, it is represented like shown in the image.

tdugard
May 31, 2005, 10:59:49
I've develloped the algorithm witch detect the coordinate of a moving object but how can I disply those coordinate, in a toolbar ?

Stefan Geissler
May 31, 2005, 11:09:26
Hello,

The toolbar and the statusbar are child windows of the mainframe. You should look into the MSDN documentation how to handle them. Good sources for MFC programming are http://www.codeguru.com and http://www.codeproject.com.

tdugard
June 1, 2005, 11:14:12
Ok thx for all your help now my last problem is to obtain the Y coordinate

X coordinate is given by the the equation :

X = i % Width (i is the position in the one dimension image)

but i can't find the equation for the Y coordinate can you help me ?

Stefan Geissler
June 1, 2005, 11:18:30
Hello,

X = i % Width;

Y = (int)(i / Width );

This gives you the current row. I did not test this, but i hope, it is correct. It is the current position divided by the length of a line, that is width.

tdugard
June 1, 2005, 12:05:00
No i't an invert Y you give me but thanks for all you are very helpful

the Y equation is :

Y = Height-1 - (i-X)%(Width-1)

Thaks for all i have finished my program,

Best Regards. Tony