在gtkmm中显示opencv cv::Mat图像

4
我希望在使用gtkmm编写的Gui中显示一个cv::Mat。所以我进行了一次测试。
我有一个小部件Gtk::Image image,我想用以下两种方法设置图像:
// first method, display from file
void displayImage1()
{
    Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_file("gtk.png");
    image.set(pixbuf);
}

// second method, display from cv::Mat
void displayImage2()
{
    cv::Mat outImage = cv::imread("gtk.png");
    cv::cvtColor(outImage, outImage, CV_BGR2RGB);
    Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(outImage.data, Gdk::COLORSPACE_RGB,false, 8, outImage.cols, outImage.rows, outImage.step);
    image.set(pixbuf);
}

第一种方法很有效。

enter image description here

然而,第二种方法效果不佳,我在屏幕上看到了一张损坏的图片,如图所示。

enter image description here

如果我将 has_alpha 参数设置为 true,结果也很奇怪(见下图)。enter image description here 使用 Gtk::DrawingArea 进行了类似的测试。使用不同的 IDE(但都是在 Linux 下使用 g++ 编译器)。所有的结果都相同。
更新:
我测试了很多图片。有时候图片会损坏,有时程序会崩溃并显示:
“程序意外终止。”

你有检查过图片是否实际上不包含alpha通道吗?我觉得除非灰色背景是图片的一部分,不然似乎是包含的。 - KjMag
@KjMag 谢谢,但是 has_alpha 为“true”的结果也不正确,我已经在问题中更新了结果。 - Summer Fang
将"bits_per_sample"改为24而不是8? - Miki
2
转换代码似乎是正确的。我认为这可能是由于数据缓冲区在函数结束时随着outImage超出范围而被释放。您需要一种实际复制gtk图像内部数据的方法。 - Miki
@SummFang - 我认为问题出在你的图像上的某些特殊性。你真的很倒霉:我卡住了,我测试了你的代码,在我的Mac Os X前置摄像头上使用cv :: VideoCapture.read的Mat确实可以工作。 - jmgonet
显示剩余6条评论
8个回答

1
尝试添加对outimage的引用,例如 outimage.addref()。我所有的问题都与此有关。源图像在 gdk_pixbuf_new_from_data 有机会映射它之前被取消引用,导致段错误、损坏等问题。只需确保稍后释放它,或使用 gdk_pixbuf_new_from_data 提供的回调即可。

1

如果您需要使用Gtkmm-2.4和OpenCv 4.6,请查看https://onthim.blogspot.com/2015/10/using-opencv-in-gtk-applications.htmlhttps://developer-old.gnome.org/gtkmm-tutorial/2.24/sec-draw-images.html.es

    Mat frame;
    frame = imread("gtk.png");    
    cv::cvtColor (frame, frame, COLOR_BGR2RGB);//COLOR_BGR2GRAY
    Glib::RefPtr<Gdk::Pixbuf> image = Gdk::Pixbuf::create_from_data(frame.data,Gdk::COLORSPACE_RGB, false, 8, frame.cols, frame.rows, frame.step);
    image->render_to_drawable(get_window(), get_style()->get_black_gc(),0, 0, 0, 0, image->get_width(), image->get_height(), // draw the whole image (from 0,0 to the full width,height) at 100,80 in the window
Gdk::RGB_DITHER_NONE, 0, 0);

0

这是我所做的,它在图像显示方面表现良好。

cvtColor(resize_image, resize_image, COLOR_BGR2RGB);
Pixbuf = Gdk::Pixbuf::create_from_data(resize_image.data, Gdk::COLORSPACE_RGB, false, 8, resize_image.cols, resize_image.rows, resize_image.step);

0
通常,这种“损坏”的图像会触发我的警报:“错误的rawstride!”。Gdk :: Pixbuf中的rawstride是数据行按字节长度。那是因为您可能有一些字节对齐约束,因此可能会在一行末尾添加一些填充。
我检查了这个 step 参数是什么,是的,在OpenCV中与Gdk :: Pixbuf中的rawstride相同。直到我意识到 outImage.step 是一个{{link1:cv:MatStep}}对象,而 Gdk :: Pixbuf :: create_from_data 期望一个int。我认为你应该使用 outImage.step [0] 代替。
请阅读{{link2:https://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat}}。

1
cv::MatStep被隐式转换为size_t,因此可以像这样使用outImage.step - Miki
谢谢您的友善回答。是的,我同意@Miki的观点。尽管如此,我尝试了outImage.step [0],但什么也没有改变。 - Summer Fang

0

开始吧:

auto lenna = imread("Lenna.png");
Image image;

cvtColor(lenna, lenna, COLOR_BGR2RGB);
auto size = lenna.size();
auto img = Gdk::Pixbuf::create_from_data(lenna.data, Gdk::COLORSPACE_RGB, lenna.channels() == 4, 8, size.width, size.height, (int) lenna.step);

image.set(img);

谢谢你的代码,但我认为它基本上和我的一样。无论如何,我还是尝试了一下,结果也是一样的。如果它在你那边能够工作,那么问题可能出在我的编译器、系统或者库版本上,我不知道 :( - Summer Fang
我认为问题在于OpenCV和GTKmm的版本不一致。请尝试使用gtkmm 2.4。 - The Moisrex

0

我认为,正如评论中 @Miki 已经建议的那样,这只是一个终身问题。

我曾经遇到过类似代码的同样问题:

{
    cv::Mat rgb;
    cv::cvtColor(src, rgb, cv::COLOR_GRAY2RGB);
    pixbuf = gdk_pixbuf_new_from_data(rgb.data,
                                      GDK_COLORSPACE_RGB, FALSE, 8,
                                      rgb.cols, rgb.rows, rgb.step,
                                      NULL, NULL);
}

以上的代码片段根本无法工作(或间歇性工作),因为引用{{link1:gdk_pixbuf_new_from_data文档}},“该数据由函数的调用者拥有”。

问题在于渲染图像时,rgb已经被销毁。在pixbuf赋值之前添加rgb.addref()可以解决该问题,但会引入内存泄漏。

一个解决方案是利用销毁回调函数取消引用Mat对象,例如:

static void
unref_mat(guchar *data, gpointer user_data)
{
    ((cv::Mat *) user_data)->release();
}

{
    cv::Mat rgb;
    cv::cvtColor(src, rgb, cv::COLOR_GRAY2RGB);
    rgb.addref()
    pixbuf = gdk_pixbuf_new_from_data(rgb.data,
                                      GDK_COLORSPACE_RGB, FALSE, 8,
                                      rgb.cols, rgb.rows, rgb.step,
                                      unref_mat, &rgb);
}

0

所以我已经测试了这个(添加scale_simple),对我来说很成功:

来源:http://orobotp.blogspot.com/2014/01/opencv-with-gtkmm3.html

版本:Gtkmm 3.22.2-2,OpenCV 4.4.0-dev,g++ 7.5.0

void displayImage2()
{
    cv::Mat outImage;
    outImage = cv::imread("gtk.png");
    cv::cvtColor(outImage, outImage, cv::COLOR_RGB2BGR);
    Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(outImage.data, Gdk::COLORSPACE_RGB,false, 8, outImage.cols, outImage.rows, outImage.step)->scale_simple( outImage.cols, outImage.rows, Gdk::INTERP_BILINEAR );
    image.set(pixbuf);
}

0

当执行超出函数 displayImage2() 的范围时,cv::Mat outImage 将被销毁,因此您应该在从 Gtk::Window 派生的自定义类中声明变量 outImage。以下是一个在 gtkmm4 和 opencv4 中正常工作的示例:

#include <opencv2/opencv.hpp>
#include <gtkmm.h>

class Buttons : public Gtk::Window
{
public:
  Buttons();
  virtual ~Buttons();

protected:
  cv::Mat outImage;
  Gtk::Image image;
};

Buttons::Buttons()
{
  outImage = cv::imread("gtk.jpg");
  cv::cvtColor(outImage, outImage, cv::COLOR_BGR2RGB);
  Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(outImage.data,
                                                                   Gdk::Colorspace::RGB,
                                                                   false, 
                                                                   8, 
                                                                   outImage.cols, 
                                                                   outImage.rows, 
                                                                   outImage.step);
  image.set(pixbuf);
  set_child(image);
}

Buttons::~Buttons()
{
}

int main(int argc, char *argv[])
{
  auto app = Gtk::Application::create("org.gtkmm.example");

  //Shows the window and returns when it is closed.
  return app->make_window_and_run<Buttons>(argc, argv);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接