Arhn - архитектура программирования

Растровое изображение искажается, когда я конвертирую объект System.Drawing.Bitmap в cv::Mat

У меня есть приложение WPF, которое делает снимок экрана работающего исполняемого файла Handbrake с помощью класса ScreenCapture, который я скопировал из переполнения стека.

public class ScreenCapture
{
    [DllImport("user32.dll")]
    static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);

    //Region Flags - The return value specifies the type of the region that the function obtains. It can be one of the following values.
    const int ERROR = 0;
    const int NULLREGION = 1;
    const int SIMPLEREGION = 2;
    const int COMPLEXREGION = 3;

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

    [DllImport("gdi32.dll")]
    static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);

    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left, Top, Right, Bottom;

        public RECT(int left, int top, int right, int bottom)
        {
            Left = left;
            Top = top;
            Right = right;
            Bottom = bottom;
        }

        public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }

        public int X
        {
            get { return Left; }
            set { Right -= (Left - value); Left = value; }
        }

        public int Y
        {
            get { return Top; }
            set { Bottom -= (Top - value); Top = value; }
        }

        public int Height
        {
            get { return Bottom - Top; }
            set { Bottom = value + Top; }
        }

        public int Width
        {
            get { return Right - Left; }
            set { Right = value + Left; }
        }

        public System.Drawing.Point Location
        {
            get { return new System.Drawing.Point(Left, Top); }
            set { X = value.X; Y = value.Y; }
        }

        public System.Drawing.Size Size
        {
            get { return new System.Drawing.Size(Width, Height); }
            set { Width = value.Width; Height = value.Height; }
        }

        public static implicit operator System.Drawing.Rectangle(RECT r)
        {
            return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
        }

        public static implicit operator RECT(System.Drawing.Rectangle r)
        {
            return new RECT(r);
        }

        public static bool operator ==(RECT r1, RECT r2)
        {
            return r1.Equals(r2);
        }

        public static bool operator !=(RECT r1, RECT r2)
        {
            return !r1.Equals(r2);
        }

        public bool Equals(RECT r)
        {
            return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
        }

        public override bool Equals(object obj)
        {
            if (obj is RECT)
                return Equals((RECT)obj);
            else if (obj is System.Drawing.Rectangle)
                return Equals(new RECT((System.Drawing.Rectangle)obj));
            return false;
        }

        public override int GetHashCode()
        {
            return ((System.Drawing.Rectangle)this).GetHashCode();
        }

        public override string ToString()
        {
            return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
        }
    }
    public Bitmap GetScreenshot(IntPtr ihandle)
    {
        IntPtr hwnd = ihandle;//handle here

        RECT rc;
        GetWindowRect(new HandleRef(null, hwnd), out rc);

        Bitmap bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
        Graphics gfxBmp = Graphics.FromImage(bmp);
        IntPtr hdcBitmap;
        try
        {
            hdcBitmap = gfxBmp.GetHdc();
        }
        catch
        {
            return null;
        }
        bool succeeded = PrintWindow(hwnd, hdcBitmap, 0);
        gfxBmp.ReleaseHdc(hdcBitmap);
        if (!succeeded)
        {
            gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
        }
        IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);
        GetWindowRgn(hwnd, hRgn);
        Region region = Region.FromHrgn(hRgn);//err here once
        if (!region.IsEmpty(gfxBmp))
        {
            gfxBmp.ExcludeClip(region);
            gfxBmp.Clear(Color.Transparent);
        }
        gfxBmp.Dispose();
        return bmp;
    }

    public void WriteBitmapToFile(string filename, Bitmap bitmap)
    {
        bitmap.Save(filename, ImageFormat.Bmp);
    }

Поэтому, когда вызывается обработчик нажатия кнопки ниже, делается скриншот окна ручного тормоза. Я записываю его на жесткий диск, чтобы убедиться, что все в порядке: скриншот ручного тормоза. Я создаю экземпляр библиотеки классов CLR ClassLibrary1::Class1 и вызываю метод DoSomething, передавая ему объект System.Drawing.Bitmap.

  private void button4_Click(object sender, RoutedEventArgs e)
  {
     string wName = "HandBrake";
     IntPtr hWnd = IntPtr.Zero;
     foreach (Process pList in Process.GetProcesses())
     {
        if (pList.MainWindowTitle.Contains(wName))
        {
           hWnd = pList.MainWindowHandle;

           var sc = new ScreenCapture();

           SetForegroundWindow(hWnd);
           var bitmap = sc.GetScreenshot(hWnd);

           sc.WriteBitmapToFile("handbrake.bmp", bitmap);

           Bitmap image1 = (Bitmap)System.Drawing.Image.FromFile("handbrake.bmp", true);

           ClassLibrary1.Class1 opencv = new ClassLibrary1.Class1();

           opencv.DoSomething(image1);
        }
     }
  }

Внутри DoSomething я пытаюсь преобразовать System.Drawing.Bitmap в класс OpenCV cv::Mat. Я вызываю cv::imwrite, чтобы убедиться, что растровое изображение все еще в порядке, к сожалению, что-то пошло не так: искаженный ручной тормоз снимок экрана

void Class1::DoSomething(Bitmap ^mybitmap)
{
cv::Mat *imgOriginal;
// Lock the bitmap's bits.  
Rectangle rect = Rectangle(0, 0, mybitmap->Width, mybitmap->Height);
Imaging::BitmapData^ bmpData = mybitmap->LockBits(rect, Imaging::ImageLockMode::ReadWrite, mybitmap->PixelFormat);
try
{
  // Get the address of the first line.
  IntPtr ptr = bmpData->Scan0;

  // Declare an array to hold the bytes of the bitmap.
  // This code is specific to a bitmap with 24 bits per pixels.
  int bytes = Math::Abs(bmpData->Stride) * mybitmap->Height;
  array<Byte>^rgbValues = gcnew array<Byte>(bytes);

  // Copy the RGB values into the array.
  System::Runtime::InteropServices::Marshal::Copy(ptr, rgbValues, 0, bytes);

  imgOriginal = new cv::Mat(mybitmap->Height, mybitmap->Width, CV_8UC3, (void *)ptr, std::abs(bmpData->Stride));
  }
  finally { mybitmap->UnlockBits(bmpData); }//Remember to unlock!!!

  cv::imwrite("from_mat.bmp", *imgOriginal);
}

Кто-нибудь может заметить мою ошибку?

03.07.2017

  • Интересно, это потому, что OpenCV обычно использует представление BGR, а не RGB? 03.07.2017

Ответы:


1

Поскольку ваше изображение растянуто по горизонтали, держу пари, что вы выбрали неправильный формат пикселей. (Он не растянут по вертикали и не перекошен по диагонали, поэтому шаг правильный.) CV_8UC3 указывает 24 бита на пиксель, но я думаю, что ваш файл BMP использует 32 бита на пиксель.

Переключите формат пикселей на CV_8UC4 или, что еще лучше, ознакомьтесь с количеством бит на пиксель изображения и на его основе выберите правильный формат CV.


Примечание: поскольку вы делаете sc.WriteBitmapToFile(), а затем opencv.DoSomething(Image.FromFile(), вся информация о том, как вы делаете снимок экрана, не имеет значения. Вы читаете растровое изображение из файла; это все, что имеет значение.

03.07.2017
  • Это сделало трюк! Большое спасибо. У меня слишком мало повторений, чтобы пометить как ответ. 04.07.2017
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..