Живое изображение с камеры в GTK

Первый:
Я работаю над программой слежения за микроскопом в темном поле. Эта программа старая (2012 г.), мы получили новую камеру, но эта камера не может быть захвачена программой захвата. Поэтому мне нужно написать «программу захвата» для этой камеры с помощью SDK (комплекта разработки программного обеспечения) от компании, производящей камеру.

Программа отслеживания написана на C и GTK 2, поэтому я хотел бы иметь «программу захвата» также на C и GTK 2, чтобы я мог просто поместить «программу захвата» в свою программу отслеживания. Я могу сделать снимок внутри «программы захвата» и показать его как gtk_image_new_from_pixbuf.

Проблема:
После того, как я смог делать снимки, я обнаружил, что темнопольный микроскоп не позволяет наводить фокус на камеру глазами. Итак, теперь мне нужно захватить «живое изображение», видео или как вы хотите это назвать.
Моя идея состояла в том, чтобы использовать таймер, чтобы камера делала снимок каждую секунду и отображала его как изображение, поэтому программа должна была удалить старое изображение и показать новое. Но этого не происходит. Я почти уверен, что с камерой проблем нет, потому что она показывает первое изображение. Я предполагаю, что pixbuf не очищен, и это приводит к тому, что новое изображение не отображается. «Программа захвата» не обязательно должна оставаться программой C/Gtk2, но, поскольку я не компьютерный ученый, я просто понятия не имею, как передать изображения в программу отслеживания, если они отличаются.

Код:
Я знаю, что gdk_pixbuf_new_from_data имеет звезду width*1, а не width*3, это не проблема.

#include <gtk/gtk.h>
#include <stdlib.h>

unsigned short *buffer = NULL;
long imageSize = 0;

void draw_call (GtkWidget *window, GdkEvent *event, gpointer data){

    (void)event; (void)data;
    GdkPixbuf *pix;
    GtkWidget* image;
    image = gtk_image_new();

    free (buffer);
    long result = NOERR;


//camera commands had to be changed 
    result = getImageSizeFromCamera( &imageSize);

    result = SetBitsPerPixel(8);

    buffer = (unsigned short *) malloc( imageSize*2);

    result = takePicture(camera, buffer, imageSize/2, NULL, NULL);

    result = stopTakingPicture (camera);
//end of camera commands

    pix = gdk_pixbuf_new_from_data ((unsigned int *) buffer, GDK_COLORSPACE_RGB,FALSE, 8,
               1936, 1460, 1936*1, pixbuf_free, NULL);

    image = gtk_image_new_from_pixbuf(pix);


    return image;
gtk_widget_unref (pix);
}

void pixbuf_free(guchar *pixels, gpointer data)
{
    g_free(pixels) ;
    fprintf(stderr, "pixbuf data freed\n") ;
}

int main (int argc, char *argv[])
{

    GdkPixbuf *pixbuf;


    // GTK

    GtkWidget *window;
    GtkWidget* image;

    image = gtk_image_new();


    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);


    gtk_window_set_title (GTK_WINDOW (window), "Image Viewer");

    g_signal_connect (window, "destroy", G_CALLBACK (Deinit), NULL);
    g_signal_connect (window, "expose-event",G_CALLBACK (draw_call), NULL);


    g_timeout_add (1000, draw_call, NULL);

    gtk_widget_set_app_paintable(window, TRUE);
    gtk_widget_set_double_buffered(window, FALSE);


    gtk_container_add(GTK_CONTAINER (window), image);

    gtk_widget_show_all (window);

    gtk_main ();


    return 0;
}

У вас уже есть изображение, и вы хотите обновить содержимое. Как насчет gtk_image_set_from_pixbuf вместо gtk_image_new_from_pixbuf? Это не имеет никакого смысла по двум причинам return image; gtk_widget_unref (pix);: 1) функция data_call имеет тип возвращаемого значения void и 2) после оператора return код больше недоступен. Кроме того, кто все равно должен использовать это возвращаемое значение?

Gerhardh 16.12.2020 14:17

Я не настолько знаком с gtk2, но я предполагаю, что вы должны передать указатель image из main в качестве пользовательского параметра в draw_call и обновить это изображение новым pixbuf.

Gerhardh 16.12.2020 14:17

gtk_image_set_from_pixbuf попробую. Что касается _unref (pix) после возвращения, я увидел его в другом посте и просто попробовал. Для функции data_call почему это void я не знал, что еще использовать. Идею с указателем image попробую.

Lordsascha 16.12.2020 15:08

Тогда этот другой код был неправильным. Основная цель return — немедленно покинуть текущую функцию и предоставить значение вызывающей стороне. Если вы покинете функцию, весь оставшийся код станет мертвым кодом. Также обработчик таймера не будет знать, что делать с вашим возвращаемым значением.

Gerhardh 16.12.2020 15:11
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
1 096
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Обновление изображений запускается обратным вызовом expose_event, который draw_call находится в вашем коде, подробнее см. Как передать веб-камеру в окно GTK?

Вы знаете, что в OpenCV реализовано отслеживание? https://docs.opencv.org/3.4/d2/d0a/tutorial_introduction_to_tracker.html

Вы также можете закодировать захват изображения в OpenCV.

OpenCV - Получение изображения с веб-камеры по умолчанию в C/C++ - Проблемы с GTK

В GUVCVIEWER реализован тот функционал, который вам нужен и возможно вы сможете портировать исходники, если ваша камера использует uvc драйвер https://github.com/jaswdr/guvcview/blob/master/gview_v4l2core/v4l2_core.c

Да, я все это знаю, но это не работает, потому что OpenCV и другие 6 программ захвата, которые я пробовал, не могут использовать мою камеру. Если бы я мог просто использовать API захвата, у меня не было бы этой проблемы.

Lordsascha 16.12.2020 15:04

guvcviewer тоже нет? Какой модуль ядра драйвера использует микроскоп?

ralf htp 16.12.2020 15:05

проверить с помощью i.e. dmesg

ralf htp 16.12.2020 15:05

Я собираю для Windows, но я проверил dmesg на ПК с Linux, и он говорит, что он использует ehci-pci, это то, что вам нужно?

Lordsascha 16.12.2020 15:28

Для Windows я этого не знаю. Существует ли /dev/video0? Убедитесь, что ваш вывод dmesg похож на раздел «Загрузка» в wiki.archlinux.org/index.php/webcam_setup. Важен материал v4l2

ralf htp 16.12.2020 15:33

Я проверил еще раз, но у меня не работает ни одна программа захвата, вероятно, потому, что компания камеры изменила команды, как вызывается камера. Спасибо, в любом случае. (Программа работает благодаря вкладу Герхарда)

Lordsascha 17.12.2020 15:32
Ответ принят как подходящий

У вас несколько проблем:

  1. У вашей функции рисования нет правильной подписи для использования в g_timer_add. Он должен вернуть TRUE, чтобы продолжить работу, и он должен принимать другие параметры.

  2. У вас есть некоторый код после возврата из этой функции, который никогда не может быть выполнен.

  3. Вы возвращаете указатель на GtkImage из обработчика таймера, который неверен. Он должен вернуться TRUE/FALSE. Обертка таймера не знает, что делать с таким указателем.

  4. Вы не обновляете какой-либо виджет самостоятельно. Я покажу это на примере.

  5. Вы создаете изображения снова и снова, в то время как вам нужно только обновить существующее.

Я расширил образец для вашего предыдущего вопроса с помощью функции обновления:

// build:
// gcc -o test `pkg-config --cflags gtk+-3.0` -Wall -Wextra test.c `pkg-config --libs gtk+-3.0`


#include <stdlib.h>
#include <stdint.h>
#include <gtk/gtk.h>

typedef struct {
  uint8_t r;
  uint8_t g;
  uint8_t b;
} rgb;


int basecolour = 0;


void pixbuf_free(guchar *pixels, gpointer data)
{
  g_free(pixels) ;
  (void)data;    
  fprintf(stderr, "pixbuf data freed\n") ;
}

gboolean draw_call(gpointer user_data)
{
    GtkImage *image = (GtkImage *) user_data;
    
  size_t Width  = 1936;
  size_t Height = 1280;
  size_t Stride_rgb = Width; // Might need rounding up

  // Put raw image data from the camera in the buffer.     
  rgb *buffer_rgb = malloc(Stride_rgb * Height * sizeof(rgb));

  for (size_t x = 0; x < Width; x++)
    for (size_t y = 0; y < Height; y++)
    {
      // Convert 1 byte greyscale in 3 byte RGB:
      uint8_t grey = (basecolour + x+y) & 0xFF;
      buffer_rgb[Stride_rgb*y + x].r = grey;
      buffer_rgb[Stride_rgb*y + x].g = grey;
      buffer_rgb[Stride_rgb*y + x].b = grey;
    }
    basecolour += 5;

  GdkPixbuf *pixbuf_rgb = gdk_pixbuf_new_from_data((guchar*)buffer_rgb, GDK_COLORSPACE_RGB,FALSE, 8,
              Width, Height, Stride_rgb*3, pixbuf_free, NULL);
  gtk_image_set_from_pixbuf(image, pixbuf_rgb);
  g_object_unref(pixbuf_rgb);

  return TRUE;  
}

int main (int argc, char *argv[])
{
    size_t Width  = 1936;
    size_t Height = 1280;
    size_t Stride_8 = Width;   // Might need rounding up
    size_t Stride_rgb = Width; // Might need rounding up

    //Due to lack of camera data, lets use some dummy data...
    uint8_t *buffer_8 = malloc(Stride_8*Height);
    for (size_t x = 0; x < Width; x++)
      for (size_t y = 0; y < Height; y++)
        buffer_8[Stride_8*y + x] = (x+y) & 0xFF;

    /*code to take raw image data from camera*/

    // Put raw image data from the camera in the buffer.     
    rgb *buffer_rgb = malloc(Stride_rgb * Height * sizeof(rgb));

    for (size_t x = 0; x < Width; x++)
      for (size_t y = 0; y < Height; y++)
      {
        // Convert 1 byte greyscale in 3 byte RGB:
        uint8_t grey = buffer_8[Stride_8*y + x];
        buffer_rgb[Stride_rgb*y + x].r = grey;
        buffer_rgb[Stride_rgb*y + x].g = grey;
        buffer_rgb[Stride_rgb*y + x].b = grey;
        }

    // GTK
    gtk_init(&argc, &argv);
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW (window), "test");
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    GdkPixbuf *pixbuf_rgb = gdk_pixbuf_new_from_data((guchar*)buffer_rgb, GDK_COLORSPACE_RGB,FALSE, 8,
               Width, Height, Stride_rgb*3, pixbuf_free, NULL);
    GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf_rgb);
    g_object_unref(pixbuf_rgb);  

    gtk_container_add(GTK_CONTAINER(window), image);
    gtk_widget_show_all(window);

    g_timeout_add (1000, draw_call, image);
    gtk_main();
    
    return 0;
}

Опять же, я добавил некоторый фиктивный код, чтобы заменить вашу часть захвата изображения. Я тестировал в Linux, и он обновляет изображение при каждом вызове обработчика таймера.

Спасибо за помощь, все работает! Но у меня это уже работало, проблема, почему я думал, что это не работает, заключалась в том, что команды от компании-производителя камеры были плохо написаны, поэтому я поместил слишком много информации в буфер, поэтому изображение было черным. Получив ваш пример кода с фиктивными данными, я узнал, что мне нужно было изменить в командах камеры. Еще раз большое спасибо.

Lordsascha 17.12.2020 15:28

Другие вопросы по теме