Ciao a tutti
Premesso che ho cercato tanti esempi e/o documentazioni, ma
- Trovo sempre lo stesso codice di esempio che fa sempre la stessa cosa;
- Il codice di cui sopra è commentato in modo minimo, giusto per farti capire cosa fa in quella determintata riga, senza spiegarti argomenti, parametri, significato di certe costanti e quant'altro;
- Microsoft come al solito non si spreca in documentazione...
vi illustro il problema.
Software semplicissimo, deve fare questo:
1) Avvia visualizzazione Webcam
2) Mostrami solo una porzione di immagine (cioè una finestra dentro la finestra webcam, di dimensione e posizione definite dal programma)
3) Acquisisci una fotografia
4) Taglia e ridimensiona
5) Salva in un formato e in un percorso prescelti.
Ecco la classe che implementa avicap32:
codice:
using System;
using System.Runtime.InteropServices;
namespace PhotoWebcam
{
/// <summary>
/// Modulo per la gestione dei messaggi alla avicap32.dll
/// </summary>
public class Capture
{
//Di seguito un elenco di variabili necessarie per dialogare con la DLL
public static short WM_CAP = 1024;
public static long WM_USER = 1024;
public static int WM_CAP_DRIVER_CONNECT = WM_CAP + 10;
public static int WM_CAP_DRIVER_DISCONNECT = WM_CAP + 11;
public static int WM_CAP_EDIT_COPY = WM_CAP + 30;
public static long WM_CAP_FILE_SAVEDIB = WM_USER + 25;
public static long WM_CAP_FILE_SAVEAS = WM_USER + 23;
public static int WM_CAP_SET_PREVIEW = WM_CAP + 50;
public static int WM_CAP_SET_PREVIEWRATE = WM_CAP + 52;
public static int WM_CAP_SET_SCALE = WM_CAP + 53;
public static int WS_CHILD = 1073741824;
public static int WS_VISIBLE = 268435456;
public static short SWP_NOMOVE = 2;
public static short SWP_NOSIZE = 1;
public static short SWP_NOZORDER = 4;
public static short HWND_BOTTOM = 1;
//creiamo delle funzioni che
//faranno da riferimento a routine esterne della nostra dll
/// <summary>
///
/// </summary>
/// <param name="hwnd"></param>
/// <param name="wMsg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("USER32.DLL")]
public static extern int SendMessageA(
int hwnd,
int wMsg,
int wParam,
[MarshalAs(UnmanagedType.AsAny)] object lParam
);
/// <summary>
/// La funzione cambia la dimensione, posizione,
/// e l'ordine della finestra e se in primo piano.
/// </summary>
/// <param name="hwnd">Handle della finestra</param>
/// <param name="hWndInsertAfter">Handle della finestra precede in ordine quella selezionata</param>
/// <param name="x">Coordinata X</param>
/// <param name="y">Coordinata Y</param>
/// <param name="cx">Dimensioni in larghezza</param>
/// <param name="cy">Dimensioni in Altezza</param>
/// <param name="wFlags">Specifica le opzioni aggiuntive</param>
/// <returns>Se ha avuto successo ritorna un valore non zero</returns>
[DllImport("USER32.DLL")]
public static extern int SetWindowPos(
int hwnd,
int hWndInsertAfter,
int x,
int y,
int cx,
int cy,
int wFlags
);
/// <summary>
/// Metodo che distrugge una finestra
/// </summary>
/// <param name="hwnd">Handle della finestra</param>
/// <returns>True o false a seconda dell'esito dell'operazione</returns>
[DllImport("USER32.DLL")]
public static extern bool DestroyWindow(int hwnd);
/// <summary>
/// Metodo che dirige il flusso alla finestra di preview
/// </summary>
/// <param name="lpszWindowName"></param>
/// <param name="dwStyle"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="nWidth"></param>
/// <param name="nHeight"></param>
/// <param name="hWndParent"></param>
/// <param name="nID"></param>
/// <returns></returns>
[DllImport("AVICAP32.dll")]
public static extern int capCreateCaptureWindowA(
string lpszWindowName,
int dwStyle,
int x,
int y,
int nWidth,
short nHeight,
int hWndParent,
int nID
);
/// <summary>
/// Funzione che ritorna l'esistenza o no di un driver
/// </summary>
/// <param name="wDriver"></param>
/// <param name="lpszName"></param>
/// <param name="cbName"></param>
/// <param name="lpszVer"></param>
/// <param name="cbVer"></param>
/// <returns></returns>
[DllImport("AVICAP32.dll")]
public static extern bool capGetDriverDescriptionA(
short wDriver,
string lpszName,
int cbName,
string lpszVer,
int cbVer
);
}
}
Punti 1,2,4,5 tutto OK!
Il punto 3 è un po' più incasinato.
Io ho una webcam che ha risoluzione massima 800x600, ma il sw deve poter operare con qualsiasi webcam.
1a domanda: Come faccio a sapere la risoluzione massima della mia webcam?
Se ridimensiono l'immagine della webcam per farla stare nella mia finestra, quando acquisisco l'immagine, quest'ultima non viene catturata a risoluzione piena, ma nelle dimensioni in cui viene visualizzata. Quindi ciò che fa è solamente un fermo immagine di ciò che visualizza. Ed io invece voglio che mi scatti una foto a risoluzione massima! Qundi:
2a domanda: Come faccio a scattare una foto a risoluzione massima?
Il metodo per catture l'immagine all'interno di una picturebox (imgGrab):
codice:
private void btnGrab_Click(object sender, EventArgs e)
{
PhotoWebcam.Capture.SendMessageA(hHwnd, Convert.ToInt32(PhotoWebcam.Capture.WM_CAP_EDIT_COPY), 0, 0);
IDataObject ido = Clipboard.GetDataObject();
if (ido.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
{
Bitmap bmpImage = (Bitmap)ido.GetData(System.Windows.Forms.DataFormats.Bitmap);
Bitmap bmpCrop = bmpImage.Clone(new Rectangle(-x, -y, imgCam.Width, imgCam.Height), bmpImage.PixelFormat);
imgGrab.Image = (Image)bmpCrop;
}
Clipboard.Clear();
}
Inoltre un altro piccolo problema che fin che lo uso io chissenefrega, ma dato che devo farlo usare ad altri non è il massimo: prima di avviare per la primissima volta la webcam, faccio scegliere all'utente il dispositivo da utilizzare con il seguente codice:
codice:
private void LoadDeviceList()
{
string strNome = Space(100); // Ho provato anche semplicemente con ""
string strVer = Space(100);
bool bReturn;
short x = 0;
//carica la lista completa nell'elenco
do
{
//ricava il nome del driver e la versione
bReturn = PhotoWebcam.Capture.capGetDriverDescriptionA(x, strNome, 100, strVer, 100);
//se un device lo aggiunge alla lista
if (bReturn)
{
lstDevices.Items.Add(strNome.Trim());
}
x += 1;
}
while (bReturn);
}
Il problema è che invece di vedere i nomi dei dispositivi, vedo righe bianche... quindi io dopo la prima volta ho capito quale era la riga che mi interessava, ma un altro potrebbe trovarsi un po' disorientato (l'elenco sembra vuoto!!)...
P.S. Ho usato avicap32.dll perchè sul web ho trovato quello, ma se c'è un altro modo va bene lo stesso