Webcam-Preview-Bild mit Scrollbars



  • Hallo,

    in diesem Beispiel geht es darum, ein Preview-Fenster von einer Webcam darzustellen. Das funktioniert auch schon, sofern das Fenster eine feste Größe hat. Nun möchte ich aber das Fenster mit Scrollbars versehen. Dazu habe ich ein Panel mit AutoScroll=true, und darin liegt die pictureBox1 mit SizeMode=AutoSize. Wenn ich eine große Bitmap in die PictureBox reinpacke, dann werden die Scrollbars angezeigt und ich kann im Bild herumscrollen. Aber mit dem Preview-Bild von der Webcam klappt das noch nicht. Es werden keine Scrollbars angezeigt, obwohl das Bild größer als das Panel ist. Wenn ich jetzt am Anfang von OpenPreviewWindow() die Zeile

    pictureBox1.Image = new Bitmap(640,480);
    

    einfüge, dann führt das dazu dass die Scrollbars angezeigt werden, und ich kann dann auch im Live-Bild herumscrollen. Aber es gibt zwei Probleme: Erstens wird da unnötig Speicher belegt, der eigentlich gar nicht gebraucht wird, und zweitens geht das schief wenn später die Kamera-Auflösung geändert wird.
    Ich brauche also zwei Dinge:
    1. Die aktuelle Kamera-Auslösung abfragen. Das sollte mit WM_CAP_GET_VIDEOFORMAT gehen, aber das lässt sich nicht so übersetzen wie ich es hingeschrieben habe. Irgendwas stimmt nicht mit dem letzten Parameter.
    2. Wenn ich die Kamera-Auflösung weiss, dann müsste ich der PictureBox irgendwie sagen wie groß das Bild ist, damit das übergeordnete Panel seine Scrollbars ein- oder ausschalten kann. Das geht zwar über

    pictureBox1.Image = new  Bitmap(breite, hoehe);
    

    aber es muss doch einen besseren Weg geben ohne soviel Speicher zu verschwenden.

    Gruß
    Michael

    public struct BITMAPINFOHEADER
            {
                public uint biSize;
                public int biWidth;
                public int biHeight;
                public ushort biPlanes;
                public ushort biBitCount;
                public uint biCompression;
                public uint biSizeImage;
                public int biXPelsPerMeter;
                public int biYPelsPerMeter;
                public uint biClrUsed;
                public uint biClrImportant;
            }
            [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
            public struct BITMAPINFO
            {
                public BITMAPINFOHEADER bmiHeader;
                public int bmiColors;
            }
    
            public BITMAPINFO m_BitmapInfo = new BITMAPINFO();
    
            // hier habe ich einiges weggelassen...
    
            private void OpenPreviewWindow()
            {
                pictureBox1.Image = new Bitmap(640,480);   // dies ist eine Hilfskonstruktion um der PictureBox zu sagen,
                                                           // wie gross das Preview-Bild ist, damit das Panel seine Scrollbars einschaltet
    
                iDevice = comboBox1.Text;
                _hHwnd = capCreateCaptureWindow(iDevice, WS_VISIBLE | WS_CHILD, 0, 0, pictureBox1.Width, pictureBox1.Height, pictureBox1.Handle.ToInt32(), 0);
                if (SendMessage(_hHwnd, WM_CAP_DRIVER_CONNECT, 0, 0) != 0)
                {
                    SendMessage(_hHwnd, WM_CAP_SET_SCALE, 0, 0);   // dritter Parameter  0: nur Ausschnitt sichtbar, volle Auflösung
                                                                   //                    1: Bild wird verkleinert in PictureBox eingepasst
                    SendMessage(_hHwnd, WM_CAP_SET_PREVIEWRATE, 40, 0);
                    SendMessage(_hHwnd, WM_CAP_SET_PREVIEW, 1, 0);
                    SetWindowPos(_hHwnd, HWND_BOTTOM, 0, 0, pictureBox1.Width, pictureBox1.Height, SWP_NOMOVE | SWP_NOZORDER);
    
                    // Abfrage der Grösse des Preview-Bildes
                    // Fehlermeldung beim letzten Parameter: "invalid argument"
                    SendMessage(_hHwnd, WM_CAP_GET_VIDEOFORMAT, Marshal.SizeOf(m_BitmapInfo), ref m_BitmapInfo);
    
                    textBox1.Text = m_BitmapInfo.bmiHeader.biWidth.ToString() + " x " + m_BitmapInfo.bmiHeader.biHeight.ToString();
                }
                else
                {
                    DestroyWindow(_hHwnd);
                }
            }
    


  • Hallo,

    micha7 schrieb:

    1. Die aktuelle Kamera-Auslösung abfragen. Das sollte mit WM_CAP_GET_VIDEOFORMAT gehen, aber das lässt sich nicht so übersetzen wie ich es hingeschrieben habe. Irgendwas stimmt nicht mit dem letzten Parameter.

    Frage 1 hat sich erledigt. Ich hatte gar nicht gemerkt, dass es zwei Versionen von SendMessage gibt, mit unterschiedlichen Parametern.

    Frage 2 ist nach wie vor aktuell. Wie sagt man einer PictureBox wie groß das Bild ist, ohne dafür ein Bitmap anzulegen?

    Gruß
    Michael



  • Hallo,

    das Problem ist gelöst. Der folgende Code kann das Preview-Fenster wahlweise mit Zoomfaktor 1x, 2x oder "Fit to Window" darstellen, wobei bei Bedarf Scrollbars verwendet werden:

    public struct BITMAPINFOHEADER
            {
                public uint biSize;
                public int biWidth;
                public int biHeight;
                public ushort biPlanes;
                public ushort biBitCount;
                public uint biCompression;
                public uint biSizeImage;
                public int biXPelsPerMeter;
                public int biYPelsPerMeter;
                public uint biClrUsed;
                public uint biClrImportant;
            }
    
            [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
            public struct BITMAPINFO
            {
                public BITMAPINFOHEADER bmiHeader;
                public int bmiColors;
            }
    
            [DllImport("user32")]
            private static extern bool SendMessage(int hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);
    
            private void previewSize()
            {
                BITMAPINFO buffer = new BITMAPINFO();
                SendMessage(_hHwnd, WM_CAP_GET_VIDEOFORMAT, Marshal.SizeOf(buffer), ref buffer);
                if (radioButton1.Checked)
                {
                    SendMessage(_hHwnd, WM_CAP_SET_SCALE, 0, 0);         // Zoom 1x
                    pictureBox1.Width = buffer.bmiHeader.biWidth;
                    pictureBox1.Height = buffer.bmiHeader.biHeight;
                }
                if (radioButton2.Checked)
                {
                    SendMessage(_hHwnd, WM_CAP_SET_SCALE, 1, 0);         // Zoom 2x
                    pictureBox1.Width = 2 * buffer.bmiHeader.biWidth;
                    pictureBox1.Height = 2 * buffer.bmiHeader.biHeight;
                }
                if (radioButton3.Checked)
                {
                    SendMessage(_hHwnd, WM_CAP_SET_SCALE, 1, 0);         // Fit to Window
                    pictureBox1.Width = panel1.Width;
                    pictureBox1.Height = panel1.Height;
                }
    
                SetWindowPos(_hHwnd, HWND_BOTTOM, 0, 0, pictureBox1.Width, pictureBox1.Height, SWP_NOMOVE | SWP_NOZORDER);
            }
    
            private void OpenPreviewWindow()
            {
                iDevice = comboBox1.Text;
                _hHwnd = capCreateCaptureWindow(iDevice, WS_VISIBLE | WS_CHILD, 0, 0, pictureBox1.Width, pictureBox1.Height, pictureBox1.Handle.ToInt32(), 0);
                if (SendMessage(_hHwnd, WM_CAP_DRIVER_CONNECT, 0, 0) != 0)
                {
                    SendMessage(_hHwnd, WM_CAP_SET_PREVIEWRATE, 40, 0);
                    SendMessage(_hHwnd, WM_CAP_SET_PREVIEW, 1, 0);
                    previewSize();
                }
                else
                {
                    DestroyWindow(_hHwnd);
                }
            }
    
            private void button4_Click(object sender, EventArgs e)
            {
                SendMessage(_hHwnd, WM_CAP_DLG_VIDEOFORMAT, 0, 0);
                previewSize();
            }
    
            private void radioButton1_CheckedChanged(object sender, EventArgs e)
            {
                previewSize();
            }
    
            private void radioButton2_CheckedChanged(object sender, EventArgs e)
            {
                previewSize();
            }
    
            private void radioButton3_CheckedChanged(object sender, EventArgs e)
            {
                previewSize();
            }
    

Anmelden zum Antworten