Reputation: 631
Sample Image:
I'm using DirectShow.net to get webcam footage into my program. To accomplish this, I'm adding the source camera to the graph, and a VideoMixingRenderer9.
That part is all working swimmingly, but the part where I extract a frame using GetCurrentImage(out lpDib) is having what I can only describe as an odd issue.
What I am doing is using Marshal.PtrToSTructure to create a BitmapInfoHeader from lpDib, then calculating the width / height / stride / & pixel format.
The problem comes when I look at the image stored in bitmap - It has a 10 px wide line down the left side that came from what is actually the right!
It is worth noting that the data I am getting from the GetCurrentImage call is actually upside down - note the call to Cap.RotateFlip.
IntPtr lpDib;
windowlessCtrl.GetCurrentImage(out lpDib);
BitmapInfoHeader head;
head = (BitmapInfoHeader)Marshal.PtrToStructure(lpDib, typeof(BitmapInfoHeader));
int width = head.Width;
int height = head.Height;
int stride = width * (head.BitCount / 8);
PixelFormat pixelFormat = PixelFormat.Format24bppRgb;
switch (head.BitCount)
{
case 24: pixelFormat = PixelFormat.Format24bppRgb; break;
case 32: pixelFormat = PixelFormat.Format32bppRgb; break;
case 48: pixelFormat = PixelFormat.Format48bppRgb; break;
default: throw new Exception("Unknown BitCount");
}
Cap = new Bitmap(width, height, stride, pixelFormat, lpDib);
Cap.RotateFlip(RotateFlipType.RotateNoneFlipY);
//if we examine Cap here (Cap.Save, for example) I'm seeing the odd stripe.
I'm completely lost here. Seems like some sort of offset issue, and I've tried tweaking with stride some, but to no avail (just creates odd diagonal look).
Upvotes: 2
Views: 2479
Reputation: 390
For those who want to avoid using SampleGrabber. The "stripe" issue can be fixed by adding the offset of the bitmap header to the IntPtr. However this requires unsafe code
IntPtr pBuffer = IntPtr.Zero;
int xBufferSize = 0;
int xWidth, xHeight;
basicVideo.get_VideoWidth(out xWidth);
basicVideo.get_VideoHeight(out xHeight);
int hr = basicVideo.GetCurrentImage(ref xBufferSize, IntPtr.Zero);
pBuffer = Marshal.AllocCoTaskMem(xBufferSize);
// Get the pixel buffer for the thumbnail
hr = basicVideo.GetCurrentImage(ref xBufferSize, pBuffer);
// Offset for BitmapHeader info
var bitmapHeader = (BitmapInfoHeader)Marshal.PtrToStructure(pBuffer, typeof(BitmapInfoHeader));
var pBitmapData = (byte*)pBuffer.ToPointer();
pBitmapData += bitmapHeader.Size;
// This will be the pointer to the bitmap pixels
var bitmapData = new IntPtr(pBitmapData);
//Change for your format type!
System.Drawing.Imaging.PixelFormat xFormat = (System.Drawing.Imaging.PixelFormat.Format32bppRgb);
int bitsPerPixel = ((int)xFormat & 0xff00) >> 8;
int bytesPerPixel = (bitsPerPixel + 7) / 8;
int stride = 4 * ((xWidth * bytesPerPixel + 3) / 4);
Bitmap image = new Bitmap(xWidth, xHeight, stride, xFormat, bitmapData);
image.RotateFlip(RotateFlipType.RotateNoneFlipY);
return image;
Calculation for Stride can be found here.
Upvotes: 0
Reputation: 21
This code is made using the DirectShowLib samples and it works:
public Bitmap GetCurrentImage()
{
Bitmap bmp = null;
if (windowlessCtrl != null)
{
IntPtr currentImage = IntPtr.Zero;
try
{
int hr = windowlessCtrl.GetCurrentImage(out currentImage);
DsError.ThrowExceptionForHR(hr);
if (currentImage != IntPtr.Zero)
{
BitmapInfoHeader structure = new BitmapInfoHeader();
Marshal.PtrToStructure(currentImage, structure);
PixelFormat pixelFormat = PixelFormat.Format24bppRgb;
switch (structure.BitCount)
{
case 24:
pixelFormat = PixelFormat.Format24bppRgb;
break;
case 32:
pixelFormat = PixelFormat.Format32bppRgb;
break;
case 48:
pixelFormat = PixelFormat.Format48bppRgb;
break;
default:
throw new Exception("BitCount desconhecido");
}
// este trecho: new IntPtr(currentImage.ToInt64() + 40), é o que resolve o problema da faixa (strip) da direita na esquerda.
bmp = new Bitmap(structure.Width, structure.Height, (structure.BitCount / 8) * structure.Width, pixelFormat, new IntPtr(currentImage.ToInt64() + 40));
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
}
catch (Exception anyException)
{
MessageBox.Show("Falha gravando imagem da Webcam: " + anyException.ToString());
}
finally
{
Marshal.FreeCoTaskMem(currentImage);
}
}
return bmp;
}
Upvotes: 2
Reputation: 2847
The video renderer is extending the bitmap to suit its own memory alignment needs, but it will have adjusted the media type to match. The VIDEOINFOHEADER (or VIDEOINFOHEADER2 structure) in the media type will have an rcTarget rectangle that defines the valid area within the larger bitmap. You can query the current media type on the input pin and get hold of this information.
You will find that the renderer only needs this extended stride for some formats, so perhaps your simplest approach is to force a different capture format. An alternative is to use the sample grabber filter instead of the VMR.
G
Upvotes: 1