Пример изображения:
Я использую DirectShow.net, чтобы получить кадры веб-камеры в моей программе. Для этого я добавляю исходную камеру к графику и VideoMixingRenderer9.
Эта часть работает плавно, но часть, где я извлекаю фрейм с помощью GetCurrentImage (из lpDib), имеет то, что я могу описать только как нечетную проблему.
Я использую Marshal.PtrToSTructure для создания BitmapInfoHeader из lpDib, а затем вычисления формата width/height/stride/and pixel.
Проблема возникает, когда я смотрю на изображение, хранящееся в растровом изображении. Он имеет ширину в 10 пикселей по левой стороне, которая исходит из того, что на самом деле правильно!
Стоит отметить, что данные, которые я получаю от вызова GetCurrentImage, фактически перевернуты - обратите внимание на вызов 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.
Я полностью потерялся здесь. Кажется, что-то вроде проблемы с смещением, и я пробовал настроить с шагами некоторые, но безрезультатно (просто создает нечетный диагональный вид).
Средство рендеринга видео расширяет растровое изображение в соответствии со своими потребностями в выравнивании памяти, но оно будет скорректировать тип носителя, который будет соответствовать. Структура VIDEOINFOHEADER (или VIDEOINFOHEADER2) в типе носителя будет иметь прямоугольник rcTarget, который определяет допустимую область в более крупном растровом изображении. Вы можете запросить текущий тип носителя на входном контакте и получить эту информацию.
Вы обнаружите, что рендерингу нужен только этот расширенный шаг для некоторых форматов, поэтому, возможно, ваш самый простой подход - заставить другой формат захвата. Альтернативой является использование фильтра захвата образца вместо VMR.
С
Этот код создается с использованием образцов DirectShowLib, и он работает:
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;
}
Для тех, кто хочет избежать использования SampleGrabber. Проблема "полоса" может быть исправлена путем добавления смещения заголовка растрового изображения к IntPtr. Однако для этого требуется небезопасный код
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;
Вычисление для Stride можно найти здесь.