Domanda: Ho il codice seguente per catturare un'immagine da una webcam.Come copiare l'immagine senza usare gli appunti?
Il mio problema è questa parte:
SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0); // copy it to the clipboard
Ciò che fa è copiare l'immagine dalla finestra negli appunti, e quindi creare un array di byte fuori di esso.
Funziona - finché non si utilizzano gli Appunti mentre il programma è in esecuzione.
Il problema è che questo non funziona nemmeno per me stesso, poiché a volte copio qualcosa mentre Visual Studio impiega anni per avviare il debug dell'applicazione Web e quindi si blocca.
Quindi, ecco la mia domanda:
Come posso ottenere l'immagine senza utilizzare gli Appunti? O più specificamente, come trasformare hCaptureWnd in System.Drawing.Image?
- Edit:
mi mancava di dire "senza creare un file, voglio un array di byte".
Si tratta di un'applicazione web, in modo che l'utente l'applicazione viene eseguita sotto non dovrebbe avere accesso in scrittura al file system (la scrittura su un file solo per la prova temporanea) ...
- Fine Edit:
/// <summary>
/// Captures a frame from the webcam and returns the byte array associated
/// with the captured image
/// </summary>
/// <param name="connectDelay">number of milliseconds to wait between connect
/// and capture - necessary for some cameras that take a while to 'warm up'</param>
/// <returns>byte array representing a bitmp or null (if error or no webcam)</returns>
private static byte[] InternalCaptureToByteArray(int connectDelay = 500)
{
Clipboard.Clear(); // clear the clipboard
int hCaptureWnd = capCreateCaptureWindowA("ccWebCam", 0, 0, 0, // create the hidden capture window
350, 350, 0, 0);
SendMessage(hCaptureWnd, WM_CAP_CONNECT, 0, 0); // send the connect message to it
Thread.Sleep(connectDelay); // sleep the specified time
SendMessage(hCaptureWnd, WM_CAP_GET_FRAME, 0, 0); // capture the frame
SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0); // copy it to the clipboard
SendMessage(hCaptureWnd, WM_CAP_DISCONNECT, 0, 0); // disconnect from the camera
Bitmap bitmap = (Bitmap)Clipboard.GetDataObject().GetData(DataFormats.Bitmap); // copy into bitmap
if (bitmap == null)
return null;
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Bmp); // get bitmap bytes
return stream.ToArray();
} // End Using stream
} // End Function InternalCaptureToByteArray
Nota (http://msdn.microsoft.com/en-us/library/windows/desktop/dd756879(v=vs.85).aspx):
HWND VFWAPI capCreateCaptureWindow(
LPCTSTR lpszWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWnd,
int nID
);
#define VFWAPI WINAPI
typedef HANDLE HWND;
typedef PVOID HANDLE;
typedef void *PVOID;
codice completo per riferimento
using System;
using System.IO;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;
// http://www.creativecodedesign.com/node/66
// http://www.barebonescoder.com/2012/01/finding-your-web-cam-with-c-directshow-net/
// http://www.codeproject.com/Articles/15219/WebCam-Fast-Image-Capture-Service-using-WIA
// http://www.c-sharpcorner.com/uploadfile/yougerthen/integrate-the-web-webcam-functionality-using-C-Sharp-net-and-com-part-viii/
// http://forums.asp.net/t/1410057.aspx
namespace cc.Utility
{
// bool isCaptured = ccWebCam.CaptureSTA("capture.jpg"); // Access to path C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\capture.jpg" denied.
// byte[] captureBytes = ccWebCam.CaptureSTA();
/// <summary>
/// Timur Kovalev (http://www.creativecodedesign.com):
/// This class provides a method of capturing a webcam image via avicap32.dll api.
/// </summary>
public static class ccWebCam
{
#region *** PInvoke Stuff - methods to interact with capture window ***
[DllImport("user32", EntryPoint = "SendMessage")]
private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
private static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle,
int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);
private const int WM_CAP_CONNECT = 1034;
private const int WM_CAP_DISCONNECT = 1035;
private const int WM_CAP_COPY = 1054;
private const int WM_CAP_GET_FRAME = 1084;
#endregion
private static object objWebCamThreadLock = new object();
//CaptureToFile(@"D:\Stefan.Steiger\Documents\Visual Studio 2010\Projects\Post_Ipag\image3.jpg"):
public static bool Capture(string filePath, int connectDelay = 500)
{
lock (objWebCamThreadLock)
{
return cc.Utility.ccWebCam.InternalCaptureAsFileInThread(filePath, connectDelay);
}
} // End Treadsafe Function Capture
public static byte[] Capture(int connectDelay = 500)
{
lock (objWebCamThreadLock)
{
return InternalCaptureToByteArrayInThread(connectDelay);
}
} // End Treadsafe Function Capture
/// <summary>
/// Captures a frame from the webcam and returns the byte array associated
/// with the captured image. The image is also stored in a file
/// </summary>
/// <param name="filePath">path the file wher ethe image will be saved</param>
/// <param name="connectDelay">number of milliseconds to wait between connect
/// and capture - necessary for some cameras that take a while to 'warm up'</param>
/// <returns>true on success, false on failure</returns>
private static bool InternalCaptureAsFileInThread(string filePath, int connectDelay = 500)
{
bool success = false;
Thread catureThread = new Thread(() =>
{
success = InternalCaptureAsFile(filePath, connectDelay);
});
catureThread.SetApartmentState(ApartmentState.STA);
catureThread.Start();
catureThread.Join();
return success;
} // End Function InternalCaptureAsFileInThread
/// <summary>
/// Captures a frame from the webcam and returns the byte array associated
/// with the captured image. The image is also stored in a file
/// </summary>
/// <param name="filePath">path the file wher ethe image will be saved</param>
/// <param name="connectDelay">number of milliseconds to wait between connect
/// and capture - necessary for some cameras that take a while to 'warm up'</param>
/// <returns>true on success, false on failure</returns>
private static bool InternalCaptureAsFile(string filePath, int connectDelay = 500)
{
byte[] capture = ccWebCam.InternalCaptureToByteArray(connectDelay);
if (capture != null)
{
// Access to path C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\image1.jpg" denied.
File.WriteAllBytes(filePath, capture);
return true;
}
return false;
} // End Function InternalCaptureAsFile
/// <summary>
/// Captures a frame from the webcam and returns the byte array associated
/// with the captured image. Runs in a newly-created STA thread which is
/// required for this method of capture
/// </summary>
/// <param name="connectDelay">number of milliseconds to wait between connect
/// and capture - necessary for some cameras that take a while to 'warm up'</param>
/// <returns>byte array representing a bitmp or null (if error or no webcam)</returns>
private static byte[] InternalCaptureToByteArrayInThread(int connectDelay = 500)
{
byte[] bytes = null;
Thread catureThread = new Thread(() =>
{
bytes = InternalCaptureToByteArray(connectDelay);
});
catureThread.SetApartmentState(ApartmentState.STA);
catureThread.Start();
catureThread.Join();
return bytes;
} // End Function InternalCaptureToByteArrayInThread
/// <summary>
/// Captures a frame from the webcam and returns the byte array associated
/// with the captured image
/// </summary>
/// <param name="connectDelay">number of milliseconds to wait between connect
/// and capture - necessary for some cameras that take a while to 'warm up'</param>
/// <returns>byte array representing a bitmp or null (if error or no webcam)</returns>
private static byte[] InternalCaptureToByteArray(int connectDelay = 500)
{
Clipboard.Clear(); // clear the clipboard
int hCaptureWnd = capCreateCaptureWindowA("ccWebCam", 0, 0, 0, // create the hidden capture window
350, 350, 0, 0);
SendMessage(hCaptureWnd, WM_CAP_CONNECT, 0, 0); // send the connect message to it
Thread.Sleep(connectDelay); // sleep the specified time
SendMessage(hCaptureWnd, WM_CAP_GET_FRAME, 0, 0); // capture the frame
SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0); // copy it to the clipboard
SendMessage(hCaptureWnd, WM_CAP_DISCONNECT, 0, 0); // disconnect from the camera
Bitmap bitmap = (Bitmap)Clipboard.GetDataObject().GetData(DataFormats.Bitmap); // copy into bitmap
if (bitmap == null)
return null;
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Bmp); // get bitmap bytes
return stream.ToArray();
} // End Using stream
} // End Function InternalCaptureToByteArray
}
}
ho provato in questo modo, ma si ottiene solo un'immagine in bianco ...
[DllImport("user32.dll")]
static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("gdi32.dll", SetLastError = true)]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
enum TernaryRasterOperations : uint
{
/// <summary>dest = source</summary>
SRCCOPY = 0x00CC0020,
/// <summary>dest = source OR dest</summary>
SRCPAINT = 0x00EE0086,
/// <summary>dest = source AND dest</summary>
SRCAND = 0x008800C6,
/// <summary>dest = source XOR dest</summary>
SRCINVERT = 0x00660046,
/// <summary>dest = source AND (NOT dest)</summary>
SRCERASE = 0x00440328,
/// <summary>dest = (NOT source)</summary>
NOTSRCCOPY = 0x00330008,
/// <summary>dest = (NOT src) AND (NOT dest)</summary>
NOTSRCERASE = 0x001100A6,
/// <summary>dest = (source AND pattern)</summary>
MERGECOPY = 0x00C000CA,
/// <summary>dest = (NOT source) OR dest</summary>
MERGEPAINT = 0x00BB0226,
/// <summary>dest = pattern</summary>
PATCOPY = 0x00F00021,
/// <summary>dest = DPSnoo</summary>
PATPAINT = 0x00FB0A09,
/// <summary>dest = pattern XOR dest</summary>
PATINVERT = 0x005A0049,
/// <summary>dest = (NOT dest)</summary>
DSTINVERT = 0x00550009,
/// <summary>dest = BLACK</summary>
BLACKNESS = 0x00000042,
/// <summary>dest = WHITE</summary>
WHITENESS = 0x00FF0062,
/// <summary>
/// Capture window as seen on screen. This includes layered windows
/// such as WPF windows with AllowsTransparency="true"
/// </summary>
CAPTUREBLT = 0x40000000
}
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
static extern bool DeleteDC(IntPtr hdc);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);
public static void ScreenshotWindow(IntPtr windowHandle)
{
Rect Rect = new Rect();
GetWindowRect(windowHandle, ref Rect);
int width = Rect.Right - Rect.Left;
int height = Rect.Bottom - Rect.Top;
IntPtr windowDeviceContext = GetWindowDC(windowHandle);
IntPtr destDeviceContext = CreateCompatibleDC(windowDeviceContext);
IntPtr bitmapHandle = CreateCompatibleBitmap(windowDeviceContext, width, height);
IntPtr oldObject = SelectObject(destDeviceContext, bitmapHandle);
BitBlt(destDeviceContext, 0, 0, width, height, windowDeviceContext, 0, 0, TernaryRasterOperations.CAPTUREBLT | TernaryRasterOperations.SRCCOPY);
SelectObject(destDeviceContext, oldObject);
DeleteDC(destDeviceContext);
ReleaseDC(windowHandle, destDeviceContext);
Image screenshot = Image.FromHbitmap(bitmapHandle);
DeleteObject(bitmapHandle);
screenshot.Save("d:\\temp\\mywebcamimage.png", System.Drawing.Imaging.ImageFormat.Png);
/*
// TODO - Remove above save when it works
using (MemoryStream stream = new MemoryStream())
{
screenshot.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
return stream.ToArray();
}
*/
}
E poi questo dopo SendMessage(hCaptureWnd, WM_CAP_GET_FRAME, 0, 0);
ScreenshotWindow(new IntPtr(hCaptureWnd));
non capisco perché non solo ... * non * copiare l'immagine negli appunti e invece convertirla direttamente in un array di byte. È il gestore di messaggi 'WM_CAP_COPY' che contiene questa logica? Che biblioteca fornisce questo messaggio? Puoi ignorare il suo comportamento? –
@Cody Gray: Quella libreria è Windows! WinAPI! E no, non posso cambiare windows, non ho il codice sorgente (non che non potrei smontare e correggere, ma questo è l'approccio sbagliato qui);) E per rispondere alla tua domanda: Perché non so come per creare un'immagine da un hCaptureWnd, che è incidentalmente la domanda ... –
Hmm, perché non riesco a trovare alcuna documentazione per questo? Immagino di essere fuori dal mio campionato qui, non ne ho mai sentito parlare. –