I have the following code where I read an image (JPG for test), specify a clipping rectangle area and write the "clipped" area out to a file.
My test image is 320 x 240 pixels # 300dpi. When I read it in all indications say that it is that size but when I write it out the result image is 102 x 76 and looking a the file properties I see no H/V resolution.
Now 320/102 = 3.1372 and 300/96 = 3.125 so is there something with screen resolution vs image?
This entire subject of writing out a IWICBitmap has been a boxing match from the beginning. Why is it this hard?
Thanks a bunch
IWICImagingFactory *pImageFactory = GfxAgent::WICImagingFactory::GetInstance().GetFactory();
D2D1_SIZE_U sizeFrame = D2D1::SizeU(imageRect.Width(), imageRect.Height());
CComPtr<IWICBitmap> pWICBitmap;
hr = pImageFactory->CreateBitmap(imageRect.Width(), imageRect.Height(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
// define the render target
CComPtr<ID2D1RenderTarget> pRenderTarget = 0;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget(pWICBitmap, rtProps, &pRenderTarget);
CComPtr<ID2D1Bitmap> imageS;
hr = GfxAgent::ImageUtilities::LoadImageFromFile(pRenderTarget, m_imgPath, 0, 0, 0, &imageS, &resX, &resY);
if (hr != S_OK)
{
}
// get format of image we just read
D2D1_PIXEL_FORMAT fmt = imageS->GetPixelFormat();
CComPtr<ID2D1Bitmap> imageD;
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU(imageRect.Width(), imageRect.Height());
// create destination image of "clipped" source image
hr = pRenderTarget->CreateBitmap(bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(fmt.format, fmt.alphaMode),
(float)resX, (float)resY), &imageD);
D2D1_POINT_2U topleft = D2D1::Point2U(0, 0);
D2D1_RECT_U srcRect = D2D1::RectU(imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
// get the "clipped" source
hr = imageD->CopyFromBitmap(&topleft, imageS, &srcRect);
if (hr != S_OK)
{
}
CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pFrame;
CComPtr<IWICStream> pStream;
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
// draw the "clipped" image into the render target (WIC image)
if (SUCCEEDED(hr)) {
pRenderTarget->BeginDraw();
pRenderTarget->Clear();
pRenderTarget->DrawBitmap(imageD);
hr = pRenderTarget->EndDraw();
}
// now proceed to write the "clipped" image to a file
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateStream(&pStream);
}
if (SUCCEEDED(hr)) {
hr = pStream->InitializeFromFilename(MultiByteToUnicode(szNewFileName).c_str(), GENERIC_WRITE);
}
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &pEncoder);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->CreateNewFrame(&pFrame, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Initialize(NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetSize((UINT)imageD->GetSize().width, (UINT)imageD->GetSize().height);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetPixelFormat(&format);
}
if (SUCCEEDED(hr)) {
hr = pFrame->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Commit();
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Commit();
}
More info
D2D1_POINT_2U topleft = D2D1::Point2U(0, 0);
D2D1_RECT_U srcRect = D2D1::RectU(imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
// get the "clipped" source
hr = imageD->CopyFromBitmap(&topleft, imageS, &srcRect);
if (hr != S_OK)
{
}
UINT wD, hD;
wD = (UINT)imageD->GetSize().width;
hD = (UINT)imageD->GetSize().height;
The imageRect is correct LTRB = 0,0,320,240
but after the copy wD = 102 and hD = 76
Why?
Here is some more info
CComPtr<ID2D1Bitmap> imageD;
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU(imageRect.Width(), imageRect.Height());
// create destination image of "clipped" source image
hr = pRenderTarget->CreateBitmap(bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(fmt.format, fmt.alphaMode),
(float)resX, (float)resY), &imageD);
UINT wD, hD;
wD = (UINT)imageD->GetSize().width;
hD = (UINT)imageD->GetSize().height;
The bitmapPixelSize is correct 320 x 240, resX and Y are 300
format= DXGI_FORMAT_B8G8R8A8_UNORM
alphaMode D2D1_ALPHA_MODE_PREMULTIPLIED
wD = 102 and hD = 76 - Why?
Latest code
IWICImagingFactory *pImageFactory = GfxAgent::WICImagingFactory::GetInstance().GetFactory();
D2D1_SIZE_U sizeFrame = D2D1::SizeU(imageRect.Width(), imageRect.Height());
CComPtr<IWICBitmap> pWICBitmap;
hr = pImageFactory->CreateBitmap(imageRect.Width(), imageRect.Height(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
// sanity check
UINT wicW, wicH;
pWICBitmap->GetSize(&wicW, &wicH);
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
// define the render target
CComPtr<ID2D1RenderTarget> pRenderTarget = 0;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget(pWICBitmap, rtProps, &pRenderTarget);
CComPtr<ID2D1Bitmap> imageS;
hr = GfxAgent::ImageUtilities::LoadImageFromFile(pRenderTarget, m_imgPath, 0, 0, 0, &imageS, &resX, &resY);
if (hr != S_OK)
{
}
// set new image resolution same as source
pWICBitmap->SetResolution(resX, resY);
// get format of image we just read
D2D1_PIXEL_FORMAT fmt = imageS->GetPixelFormat();
CComPtr<ID2D1Bitmap> imageD;
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU(imageRect.Width(), imageRect.Height());
// create destination image of "clipped" source image
hr = pRenderTarget->CreateBitmap(bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(fmt.format, fmt.alphaMode),
(float)resX, (float)resY), &imageD);
D2D1_POINT_2U topleft = D2D1::Point2U(0, 0);
D2D1_RECT_U srcRect = D2D1::RectU(imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
// get the "clipped" source
hr = imageD->CopyFromBitmap(&topleft, imageS, &srcRect);
if (hr != S_OK)
{
}
// just a sanity check (pixels NOT DIPS)
D2D1_SIZE_U sourcePixelSize = imageS->GetPixelSize();
D2D1_SIZE_U destPixelSize = imageD->GetPixelSize();
CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pFrame;
CComPtr<IWICStream> pStream;
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
// draw the "clipped" image into the render target (WIC image)
if (SUCCEEDED(hr)) {
pRenderTarget->BeginDraw();
pRenderTarget->Clear();
pRenderTarget->DrawBitmap(imageD);
hr = pRenderTarget->EndDraw();
}
// now proceed to write the "clipped" image to a file
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateStream(&pStream);
}
if (SUCCEEDED(hr)) {
hr = pStream->InitializeFromFilename(MultiByteToUnicode(szNewFileName).c_str(), GENERIC_WRITE);
}
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &pEncoder);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->CreateNewFrame(&pFrame, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Initialize(NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetSize(destPixelSize.width, destPixelSize.height);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetPixelFormat(&format);
}
if (SUCCEEDED(hr)) {
hr = pFrame->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Commit();
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Commit();
}
ID2D1Bitmap::GetSize gets you DIPs:
Returns the size, in device-independent pixels (DIPs), of the bitmap.
A DIP is 1/96 of an inch. To retrieve the size in device pixels, use the ID2D1Bitmap::GetPixelSize method.
In your case the size is 320 px * 96 dpi/px / 300 dpi = 102.4 device-independent pixels. The same along Y axis.
Knowing what the original image's resolution is when I create the render target from the WICBitmap I used the following properties
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
rtProps.dpiX = (float)m_img.GetResolutionX();
rtProps.dpiY = (float)m_img.GetResolutionY();
the key was using the resolution of the original image in the create, attempting to set them later did nothing. Roman gave me the "hint" - Thanks
Related
I am using Window Media Foundation to take a photo in my C++ application.
My problem is about the long waiting time for sampling.
The algorithm for taking pictures:
Configure preview (frame size for example 640 x 480)
Start preview
User call a reqest to teke photo
call my method ConfigurePhoto (frame size for example 2560 x 1920)
call IMFCaptureEngine::TakePhoto.
to receive photo aplication uses my class which implement interface IMFCaptureEngineOnSampleCallback.
and it waits on call HRESULT STDMETHODCALLTYPE CaptureEngineSampleCB::OnSample(IMFSample *pSample)
I have got a problem with long time to wait for step 6.
Sometimes I wait only one second, but sometimes I wait few minuts.
The image on the preview looks stuck at this time.
Is it possible to shorten the waiting time?
Does anyone know what I'm doing wrong?
Is better method to capture photo?
Below some code
HRESULT CaptureDevice::ConfigurePhoto(INT resolutionId, ConfigurationType configureType)
{
HRESULT hr = S_OK;
{
IMFCaptureSink *pSink = NULL;
IMFMediaSink *p = NULL;
UINT32 width, height;
// Get a pointer to the photo sink.
HRESULT hr = m_pEngine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, &pSink);
if (SUCCEEDED(hr))
{
IMFCapturePhotoSink *pPhoto = NULL;
hr = pSink->QueryInterface(IID_PPV_ARGS(&pPhoto));
if (SUCCEEDED(hr))
{
IMFCaptureSource *pSource;
hr = m_pEngine->GetSource(&pSource);
if (SUCCEEDED(hr))
{
IMFMediaType *pMediaType = 0;
hr = pSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, resolutionId, &pMediaType);
if (SUCCEEDED(hr))
{
LogMediaType(pMediaType);
hr = pSource->SetCurrentDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, pMediaType);
if (SUCCEEDED(hr))
{
if (WaitForMediaTypeChange())
{
IMFMediaType *pMediaType = 0;
//Utils::SafeRelease(&pMediaType);
hr = pSource->GetCurrentDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, &pMediaType);
if (SUCCEEDED(hr))
{
hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
IMFMediaType *pPhotoMediaType = 0;
LONG bufferStride = 0;
//Configure the photo format
hr = Utils::CreateSnapshotMediaTypeForCB(pMediaType, &pPhotoMediaType, bufferStride);
if (SUCCEEDED(hr))
{
//LogMediaType(pPhotoMediaType);
if (SUCCEEDED(hr))
{
hr = pPhoto->RemoveAllStreams();
if (SUCCEEDED(hr))
{
DWORD dwSinkStreamIndex;
// Try to connect the first still image stream to the photo sink
hr = pPhoto->AddStream((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, pPhotoMediaType, NULL, &dwSinkStreamIndex);
if (SUCCEEDED(hr))
{
hr = pPhoto->SetSampleCallback(m_pSampleCallback);
if (SUCCEEDED(hr))
{
hr = pPhoto->Prepare();
if (SUCCEEDED(hr))
{
if (!WaitForCaptureSinkPrepare())
{
hr = E_FAIL;
}
}
}
}
}
}
Utils::SafeRelease(&pPhotoMediaType);
}
}
Utils::SafeRelease(&pMediaType);
}
else
{
hr = E_FAIL;
}
}
Utils::SafeRelease(&pMediaType);
}
Utils::SafeRelease(&pSource);
}
Utils::SafeRelease(&pPhoto);
}
Utils::SafeRelease(&pSink);
}
/* ... other action */
}
return hr;
}
HRESULT CaptureDevice::CreatePreview(INT previewRresolutionId, HWND hWnd, BOOL mirror)
{
HRESULT hr = S_OK;
DWORD dwSinkStreamIndex = 0;
UINT32 w, h;
LONG lStride = 0;
if (m_pPreview == NULL)
{
IMFCaptureSink *pSink = NULL;
hr = m_pEngine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, &pSink);
if (SUCCEEDED(hr))
{
hr = pSink->QueryInterface(IID_PPV_ARGS(&m_pPreview));
if (SUCCEEDED(hr))
{
//if (m_useDirect3D)
{
hr = m_pPreview->SetMirrorState(mirror);
m_previewWnd = PreviewWindow::CreatePreviewWindow(*this, hWnd);
hr = m_pPreview->SetRenderHandle(m_previewWnd);
}
if (SUCCEEDED(hr))
{
IMFCaptureSource* pSource = NULL;
hr = m_pEngine->GetSource(&pSource);
if (SUCCEEDED(hr))
{
IMFMediaType *pMediaType = NULL;
// Configure the video format for the preview sink.
hr = pSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, previewRresolutionId, &pMediaType);
if (SUCCEEDED(hr))
{
LogMediaType(pMediaType);
//hr = Utils::SetDefaultStride(pMediaType, &lStride);
hr = pSource->SetCurrentDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, pMediaType);
if (SUCCEEDED(hr))
{
WaitForMediaTypeChange();
IMFMediaType *pMediaType2 = NULL;
hr = Utils::CloneVideoMediaType(pMediaType, MFVideoFormat_RGB24, &pMediaType2);
if (SUCCEEDED(hr))
{
hr = pMediaType2->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
if (SUCCEEDED(hr))
{
// Connect the video stream to the preview sink.
hr = m_pPreview->AddStream((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, pMediaType2, NULL, &dwSinkStreamIndex);
}
}
Utils::SafeRelease(&pMediaType2);
}
}
Utils::SafeRelease(&pMediaType);
}
Utils::SafeRelease(&pSource);
}
}
}
Utils::SafeRelease(&pSink);
if (FAILED(hr))
{
Utils::SafeRelease(&m_pPreview);
}
}
return hr;
}
Attributes of the preview stream:
MF_MT_FRAME_SIZE 640 x 480
MF_MT_AVG_BITRATE 110592000
MF_MT_MAJOR_TYPE MFMediaType_Video
MF_MT_DEFAULT_STRIDE 640
MF_MT_AM_FORMAT_TYPE {05589F80-C356-11CE-BF01-00AA0055595A}
MF_MT_FIXED_SIZE_SAMPLES 1
MF_MT_FRAME_RATE 30000 x 1001
MF_MT_PIXEL_ASPECT_RATIO 1 x 1
MF_MT_ALL_SAMPLES_INDEPENDENT 1
MF_MT_FRAME_RATE_RANGE_MIN 30000 x 1001
MF_MT_SAMPLE_SIZE 460800
MF_MT_INTERLACE_MODE 2
MF_MT_FRAME_RATE_RANGE_MAX 30000 x 1001
MF_MT_SUBTYPE MFVideoFormat_NV12
the same as
captionSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, mediaTypeIndex /* chosen by user index 0*/, &pMediaType);
Attributes of the image stream:
MF_MT_FRAME_SIZE 2560 x 1920
MF_MT_MAJOR_TYPE MFMediaType_Image
MF_MT_AM_FORMAT_TYPE {692FA379-D3E8-4651-B5B4-0B94B013EEAF}
MF_MT_ALL_SAMPLES_INDEPENDENT 1
MF_MT_SUBTYPE {19E4A5AA-5662-4FC5-A0C0-1758028E1057}
The same as
captionSource->GetAvailableDeviceMediaType( (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, mediaTypeIndex /* chosen by user index 16*/, &pMediaType);
I have taken a code from the net to capture a frame from a video file and modified to capture all frames and store it as bmp images.
HRESULT GrabVideoBitmap(PCWSTR pszVideoFile)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
long evCode;
wchar_t temp[10];
wchar_t framename[50] = IMAGE_FILE_PATH; // L"D:\\sampleframe";
BYTE *pBuffer = NULL;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
return 0;
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGraph));
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
// Displays the metadata of the file
DisplayFileInfo((wchar_t*)pszVideoFile); // to display video information
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
hr = pSourceF->EnumPins(&pEnum);
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
hr = pGraph->AddFilter(pNullF, L"Null Filter");
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
hr = pGrabber->SetOneShot(TRUE);
hr = pGrabber->SetBufferSamples(TRUE);
hr = pControl->Run();
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
for (int i = 0; i < 10; i++)
{
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
hr = pGrabber->GetConnectedMediaType(&mt);
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
swprintf(temp, 5, L"%d", i);
wcscat_s(framename, temp);
wcscat_s(framename, L".bmp");
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap((PCWSTR)framename, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
wcscpy_s(framename, IMAGE_FILE_PATH);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
FreeMediaType(mt);
}
done:
CoTaskMemFree(pBuffer);
SafeRelease(&pPin);
SafeRelease(&pEnum);
SafeRelease(&pNullF);
SafeRelease(&pSourceF);
SafeRelease(&pGrabber);
SafeRelease(&pGrabberF);
SafeRelease(&pControl);
SafeRelease(&pEvent);
SafeRelease(&pGraph);
return hr;
}
The input video file has 132 frames.
But only 68 images are generated.
Also last frame of the video is captured for the last 38 images.
I think the directshow graph is running continuously and WriteBitmap() is missing frames.
How to get the control in directX to capture one frame and write it to bmp file and capture the next frame and thus capture all the frames as bmp images.
Thanks
Arun
Your approach is wrong. Currently, you set the sample grabber to one shot and after that you wait for the graph completion. This way it only works for capturing a single frame. You need to capture the frames inside the ISampleGrabberCB callback of your pGrabber. You need to implement ISampleGrabberCB interface and use ISampleGrabber::SetCallback on your pGrabber filter to point it to your implementation. After that you can capture the frames inside either SampleCB or BufferCB methods. http://www.infognition.com/blog/2013/accessing_raw_video_in_directshow.html
I'm trying to draw a bitmap to the screen; the code that calls to load the bitmap from a file works, but it seems the actual drawing part is causing some program crashes.
I followed a tutorial from MSDN, but the only change I made was instead of using ID2D1RenderTarget, I'm using a ID2D1DCRenderTarget.
Here's the Load method that works:
HRESULT LoadBitmapFromFile(
ID2D1DCRenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR uri,
UINT destinationWidth,
UINT destinationHeight,
ID2D1Bitmap **ppBitmap
)
{
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pSource = NULL;
IWICStream *pStream = NULL;
IWICFormatConverter *pConverter = NULL;
IWICBitmapScaler *pScaler = NULL;
HRESULT hr = pIWICFactory->CreateDecoderFromFilename(
uri,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&pDecoder
);
if (SUCCEEDED(hr))
{
// Create the initial frame.
hr = pDecoder->GetFrame(0, &pSource);
}
if (SUCCEEDED(hr))
{
// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
hr = pIWICFactory->CreateFormatConverter(&pConverter);
}
if (SUCCEEDED(hr))
{
// If a new width or height was specified, create an
// IWICBitmapScaler and use it to resize the image.
if (destinationWidth != 0 || destinationHeight != 0)
{
UINT originalWidth, originalHeight;
hr = pSource->GetSize(&originalWidth, &originalHeight);
if (SUCCEEDED(hr))
{
if (destinationWidth == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationHeight) / static_cast<FLOAT>(originalHeight);
destinationWidth = static_cast<UINT>(scalar * static_cast<FLOAT>(originalWidth));
}
else if (destinationHeight == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationWidth) / static_cast<FLOAT>(originalWidth);
destinationHeight = static_cast<UINT>(scalar * static_cast<FLOAT>(originalHeight));
}
hr = pIWICFactory->CreateBitmapScaler(&pScaler);
if (SUCCEEDED(hr))
{
hr = pScaler->Initialize(
pSource,
destinationWidth,
destinationHeight,
WICBitmapInterpolationModeCubic
);
}
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(
pScaler,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
}
else // Don't scale the image.
{
hr = pConverter->Initialize(
pSource,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
if (SUCCEEDED(hr))
{
// Create a Direct2D bitmap from the WIC bitmap.
hr = pRenderTarget->CreateBitmapFromWicBitmap(
pConverter,
NULL,
ppBitmap
);
}
if (pDecoder) pDecoder->Release();
if (pSource) pSource->Release();
if (pStream) pStream->Release();
if (pScaler) pScaler->Release();
return hr;
}
And in between the BeginDraw() and EndDraw() for the render target, I have the following code to draw the bitmap. This is the part that crashes the program when I attempt to run it:
d2dg->pRT->BeginDraw();
// Create a WIC Factory
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&wicFactory);
hr = LoadBitmapFromFile(d2dg->pRT, wicFactory, L"image1.png", 400, 400, &pBitmap);
D2D1_SIZE_F size = pBitmap->GetSize();
D2D1_POINT_2F upperLeftCorner = D2D1::Point2F(100.f, 10.f);
// Draw a bitmap.
d2dg->pRT->DrawBitmap(
pBitmap,
D2D1::RectF(
upperLeftCorner.x,
upperLeftCorner.y,
upperLeftCorner.x + size.width,
upperLeftCorner.y + size.height)
);
d2dg->pRT->EndDraw();
Found the issue to this: the bitmap I wanted to draw was still NULL.
This can be fixed by deleting the ID2D1Bitmap **ppBitmap parameter from LoadBitmapFromFile and just work with the pointer to &pBitmap for any references to a bitmap within the method.
That way, pBitmap gets manipulated and will not remain NULL.
Scenario:
Load a TIFF image and extract the frames of tiff image and save it locally.
Combine the extracted frames to the output TIFF image.
When i try to combine the frames, the size of the output tiff image is increasing drastically. For example if my input size if 40 MB , the output is increased to 300 MB.
Below is the code which explains the scenario,
void readTiff()
{
HRESULT hr;
IWICBitmapFrameDecode *frameDecode = NULL;
IWICFormatConverter *formatConverter = NULL;
IWICBitmapEncoder *encoder = NULL;
IWICStream *pOutStream = NULL;
IWICBitmapFrameEncode *frameEncode = NULL;
IWICImagingFactory* m_pWICFactory;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pWICFactory)
);
IWICBitmapDecoder *pIDecoder = NULL;
hr = m_pWICFactory->CreateDecoderFromFilename(
L"D:\\test28\\Multitiff_files\\300dpiTIFF40MB_WATER.tif", // Image to be decoded
NULL, // Do not prefer a particular vendor
GENERIC_READ, // Desired read access to the file
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
&pIDecoder // Pointer to the decoder
);
UINT frameCount = 0;
pIDecoder->GetFrameCount(&frameCount);
for (int i = 0; i < frameCount; i++)
{
wchar_t temp[200];
int j = i;
swprintf_s(temp, 200, L"D:\\test28\\Multitiff_files\\out\\filename_png%d.jpeg", i);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateStream(&pOutStream);
if (SUCCEEDED(hr))
hr = pOutStream->InitializeFromFilename(temp, GENERIC_WRITE);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &encoder);
if (SUCCEEDED(hr))
hr = encoder->Initialize(pOutStream, WICBitmapEncoderNoCache);
hr = pIDecoder->GetFrame(i, &frameDecode);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateFormatConverter(&formatConverter);
hr = formatConverter->Initialize(
frameDecode, // Source frame to convert
GUID_WICPixelFormat8bppIndexed, // The desired pixel format
WICBitmapDitherTypeNone, // The desired dither pattern
NULL, // The desired palette
0.f, // The desired alpha threshold
WICBitmapPaletteTypeMedianCut // Palette translation type
);
IPropertyBag2 *pPropertybag = NULL;
//Create a new frame..
hr = encoder->CreateNewFrame(&frameEncode, &pPropertybag);
//PROPBAG2 option = { 0 };
//option.pstrName = L"ImageQuality";
//VARIANT varValue;
//VariantInit(&varValue);
//varValue.vt = VT_R4;
//varValue.fltVal = 0.01f;
//hr = pPropertybag->Write(
// 1, // number of properties being set
// &option,
// &varValue);
WICPixelFormatGUID pixelFormat;
hr = frameEncode->Initialize(pPropertybag);
//pixelFormat = GUID_WICPixelFormat8bppIndexed;
frameDecode->GetPixelFormat(&pixelFormat);
hr = frameEncode->SetPixelFormat(&pixelFormat);
hr = frameEncode->WriteSource(formatConverter, NULL);
frameEncode->Commit();
encoder->Commit();
if (formatConverter)
formatConverter->Release();
if (frameDecode)
frameDecode->Release();
if (frameEncode)
frameEncode->Release();
if (pOutStream)
pOutStream->Release();
if (encoder)
encoder->Release();
}
if (m_pWICFactory)
m_pWICFactory->Release();
pIDecoder->Release();
}
void combineFile(int count)
{
HRESULT hr;
IWICImagingFactory *pFactory = NULL;
IWICStream *pInStream = NULL;
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *frameDecode = NULL;
IWICFormatConverter *formatConverter = NULL;
IWICBitmapEncoder *encoder = NULL;
IWICStream *pOutStream = NULL;
IWICBitmapFrameEncode *frameEncode = NULL;
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
if (!SUCCEEDED(hr)) {
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
}
if (SUCCEEDED(hr))
hr = pFactory->CreateStream(&pOutStream);
if (SUCCEEDED(hr))
hr = pOutStream->InitializeFromFilename(L"D:\\test28\\Multitiff_files\\out\\out.tiff", GENERIC_WRITE);
if (SUCCEEDED(hr))
hr = pFactory->CreateEncoder(GUID_ContainerFormatWmp, NULL, &encoder);
if (SUCCEEDED(hr))
hr = encoder->Initialize(pOutStream, WICBitmapEncoderNoCache);
for (int i = 0; i < count; i++)
{
wchar_t temp[200];
swprintf_s(temp, 200, L"D:\\test28\\Multitiff_files\\out\\filename_png%d.jpeg", i);
if (SUCCEEDED(hr))
hr = pFactory->CreateStream(&pInStream);
if (SUCCEEDED(hr))
hr = pInStream->InitializeFromFilename(temp, GENERIC_READ);
if (SUCCEEDED(hr))
hr = pFactory->CreateDecoderFromStream(pInStream, NULL, WICDecodeMetadataCacheOnLoad, &decoder);
if (SUCCEEDED(hr))
hr = pFactory->CreateFormatConverter(&formatConverter);
hr = decoder->GetFrame(0, &frameDecode);
//hr = formatConverter->Initialize(
// frameDecode, // Source frame to convert
// GUID_WICPixelFormat8bppIndexed, // The desired pixel format
// WICBitmapDitherTypeNone, // The desired dither pattern
// NULL, // The desired palette
// 0.f, // The desired alpha threshold
// WICBitmapPaletteTypeMedianCut // Palette translation type
// );
WICPixelFormatGUID pixelFormat;
frameDecode->GetPixelFormat(&pixelFormat);
IPropertyBag2 *pPropertybag = NULL;
//Create a new frame..
hr = encoder->CreateNewFrame(&frameEncode, &pPropertybag);
PROPBAG2 option = { 0 };
option.pstrName = L"TiffCompressionMethod";
VARIANT varValue;
VariantInit(&varValue);
varValue.vt = VT_UI1;
varValue.bVal = WICTiffCompressionOption::WICTiffCompressionZIP;
hr = pPropertybag->Write(1, &option, &varValue);
hr = frameEncode->Initialize(pPropertybag);
hr = frameEncode->SetPixelFormat(&pixelFormat);
hr = frameEncode->WriteSource(formatConverter, NULL);
hr = frameEncode->Commit();
if (pInStream)
pInStream->Release();
if (decoder)
decoder->Release();
if (formatConverter)
formatConverter->Release();
if (frameEncode)
frameEncode->Release();
//if (frameDecode)
//frameDecode->Release();
}
encoder->Commit();
if (pFactory)
pFactory->Release();
if (encoder)
encoder->Release();
if (pOutStream)
pOutStream->Release();
}
One easy way to debug this kind of issue is to use ImageMagick which is installed on most Linux distros and is available for OSX and Windows. First use the identify utility within the suite with its verbose option to find out everything about your before and after images like this:
# Find out all we know about first image and put in file "1.txt"
identify -verbose image1.tif > 1.txt
# Find out all we know about second image and put in file "2.txt"
identify -verbose image2.tif > 2.txt
Now use your favourite file comparison tool to see the differences:
opendiff 1.txt 2.txt
I just try to add LoadBitmapFromFile function from sample (from SDK) to my MFC project. So my app fails during runtime on line, no errors, no warnings.
hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, NULL, ppBitmap);
When I debugging it and goes over this line I receive empty window and debug session stops. How to find out what the error is? What could be the cause?
HRESULT DemoApp::LoadBitmapFromFile(
ID2D1RenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR uri,
UINT destinationWidth,
UINT destinationHeight,
ID2D1Bitmap **ppBitmap)
{
HRESULT hr = S_OK;
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pSource = NULL;
IWICStream *pStream = NULL;
IWICFormatConverter *pConverter = NULL;
IWICBitmapScaler *pScaler = NULL;
hr = pIWICFactory->CreateDecoderFromFilename(
uri,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&pDecoder
);
if (SUCCEEDED(hr))
{
// Create the initial frame.
hr = pDecoder->GetFrame(0, &pSource);
}
if (SUCCEEDED(hr))
{
// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
hr = pIWICFactory->CreateFormatConverter(&pConverter);
}
if (SUCCEEDED(hr))
{
// If a new width or height was specified, create an
// IWICBitmapScaler and use it to resize the image.
if (destinationWidth != 0 || destinationHeight != 0)
{
UINT originalWidth, originalHeight;
hr = pSource->GetSize(&originalWidth, &originalHeight);
if (SUCCEEDED(hr))
{
if (destinationWidth == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationHeight) / static_cast<FLOAT>(originalHeight);
destinationWidth = static_cast<UINT>(scalar * static_cast<FLOAT>(originalWidth));
}
else if (destinationHeight == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationWidth) / static_cast<FLOAT>(originalWidth);
destinationHeight = static_cast<UINT>(scalar * static_cast<FLOAT>(originalHeight));
}
hr = pIWICFactory->CreateBitmapScaler(&pScaler);
if (SUCCEEDED(hr))
{
hr = pScaler->Initialize(
pSource,
destinationWidth,
destinationHeight,
WICBitmapInterpolationModeCubic
);
}
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(
pScaler,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
}
else // Don't scale the image.
{
hr = pConverter->Initialize(
pSource,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
if (SUCCEEDED(hr))
{
// Create a Direct2D bitmap from the WIC bitmap.
hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, NULL, ppBitmap);
}
SafeRelease(&pDecoder);
SafeRelease(&pSource);
SafeRelease(&pStream);
SafeRelease(&pConverter);
SafeRelease(&pScaler);
return hr;
}