Câu hỏi: Tôi có mã dưới đây để chụp ảnh từ webcam.Làm thế nào để sao chép hình ảnh mà không cần sử dụng clipboard?
Vấn đề của tôi là phần này:
SendMessage(hCaptureWnd, WM_CAP_COPY, 0, 0); // copy it to the clipboard
gì nó làm là sao chép hình ảnh từ cửa sổ vào clipboard, và sau đó tạo ra một mảng byte ra khỏi nó.
Tính năng này hoạt động - miễn là bạn không sử dụng khay nhớ tạm trong khi chương trình đang chạy.
Vấn đề là, điều này thậm chí không làm việc cho bản thân mình, như tôi đôi khi sao chép một cái gì đó trong khi Visual Studio mất lứa tuổi để bắt đầu gỡ lỗi ứng dụng web, và sau đó nó bị treo.
Vì vậy, ở đây câu hỏi của tôi:
Làm cách nào để có được hình ảnh mà không cần sử dụng khay nhớ tạm? Hoặc cụ thể hơn, làm thế nào để chuyển đổi hCaptureWnd thành System.Drawing.Image?
- Chỉnh sửa:
Tôi quên nói "không tạo tệp, tôi muốn mảng byte".
Đó là một ứng dụng web, vì vậy người dùng các ứng dụng chạy dưới không nên có quyền ghi vào hệ thống tập tin (văn bản vào một tập tin duy nhất để thử nghiệm tạm thời) ...
- End 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
Note (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;
Full mã cho tài liệu tham khảo
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
}
}
tôi đã cố gắng như thế này, nhưng nó chỉ được một hình ảnh đen ...
[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();
}
*/
}
Và rồi sau này SendMessage(hCaptureWnd, WM_CAP_GET_FRAME, 0, 0);
ScreenshotWindow(new IntPtr(hCaptureWnd));
Tôi không hiểu tại sao bạn không chỉ ... * không * sao chép hình ảnh vào clipboard và thay vào đó chuyển đổi nó trực tiếp đến một mảng byte. Có phải trình xử lý tin nhắn 'WM_CAP_COPY' có chứa logic này không? Thư viện nào cung cấp thư này? Bạn có thể ghi đè hành vi của nó không? –
@Cody Grey: Thư viện đó là Windows! WinAPI! Và không, tôi không thể thay đổi cửa sổ, tôi không có mã nguồn (không phải là tôi không thể tháo rời và vá, nhưng đó là cách tiếp cận sai ở đây);) Và để trả lời câu hỏi của bạn: Bởi vì tôi không biết làm thế nào để tạo một hình ảnh từ hCaptureWnd, đó là vấn đề đặt ra ... –
Hmm, tại sao tôi không thể tìm thấy bất kỳ tài liệu nào về nó? Đoán tôi ra khỏi giải đấu của tôi ở đây, tôi chưa bao giờ nghe nói về nó. –