[solved] Build OpenCV with gtk and python

Hi,

I’m trying to get OpenCV in python to show a GUI. I have a default.nix that I invoke with nix-shell:

with import <nixpkgs> {};
(let

foo="bar";

in

stdenv.mkDerivation {
  name = "impurePythonEnv";
  buildInputs = [
    (python37.buildEnv.override {
      extraLibs = [
	pkgs.python37Packages.matplotlib
	pkgs.python37Packages.numpy
	pkgs.python37Packages.scipy
	pkgs.python37Packages.gnureadline        
        pkgs.python37Packages.opencv4
      ];
      ignoreCollisions = true;
    })
  ];
  shellHook = ''
        # set SOURCE_DATE_EPOCH so that we can use python wheels
        SOURCE_DATE_EPOCH=$(date +%s)
        export LANG=en_US.UTF-8	
  '';
})

Unfortunately this doesn’t work:

Traceback (most recent call last):
  File "test_webcam.py", line 18, in <module>
    cv.imshow('frame', gray)
cv2.error: OpenCV(4.1.2) /build/source/modules/highgui/src/window.cpp:651: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvShowImage'

I’m trying to marshall the information in this:

into my default.nix above, but I don’t really have enough of a feel for nix-the-language for this to work. Could anyone help me please?

Thanks,

Nawal.

1 Like

Example attempt (no re-building happens, same error as before):

with import <nixpkgs> {};
(

let python =
    let
    packageOverrides = self:
    super: {
      opencv4 = super.opencv4.override {
        enableGtk2 = true;
        gtk2 = pkgs.gtk2;
        enableFfmpeg = true; #here is how to add ffmpeg and other compilation flags
        ffmpeg_3 = pkgs.ffmpeg-full;
        };
    };
    in
      pkgs.python37.override {inherit packageOverrides; self = python;};

in

stdenv.mkDerivation {
  name = "impurePythonEnv";
  buildInputs = [
    (python37.buildEnv.override {
      extraLibs = [
	pkgs.python37Packages.matplotlib
	pkgs.python37Packages.numpy
	pkgs.python37Packages.scipy
	pkgs.python37Packages.gnureadline        
        pkgs.python37Packages.opencv4
      ];
      ignoreCollisions = true;
    })
  ];
  shellHook = ''
        # set SOURCE_DATE_EPOCH so that we can use python wheels
        SOURCE_DATE_EPOCH=$(date +%s)
        export LANG=en_US.UTF-8	
  '';
})

You are defining new python variable but you still use non-overridden python37 and pkgs.python37Packages in the derivation. Try replacing them with python and python.pkgs, respectively.

1 Like

Thank you, will try that!

Thanks @jtojnar , it worked!

with import <nixpkgs> {};
(

let python =
    let
    packageOverrides = self:
    super: {
      opencv4 = super.opencv4.override {
        enableGtk2 = true;
        gtk2 = pkgs.gtk2;
        enableFfmpeg = true; #here is how to add ffmpeg and other compilation flags
        };
    };
    in
      pkgs.python37.override {inherit packageOverrides; self = python;};

in

stdenv.mkDerivation {
  name = "impurePythonEnv";
  buildInputs = [
    imagemagick
    v4l-utils
    (python37.buildEnv.override {
      extraLibs = [
	pkgs.python37Packages.matplotlib
	pkgs.python37Packages.numpy
	pkgs.python37Packages.scipy
	pkgs.python37Packages.gnureadline        
        python.pkgs.opencv4
      ];
      ignoreCollisions = true;
    })
  ];
  shellHook = ''
        # set SOURCE_DATE_EPOCH so that we can use python wheels
        SOURCE_DATE_EPOCH=$(date +%s)
        export LANG=en_US.UTF-8	
  '';
})

3 Likes

If anyone comes here in search for how to spawn a nix-shell with opencv-python and GTK (UI) enabled (like I did), the simple solution is:

{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  name = "opencv-python-gtk";
  buildInputs = with pkgs; [
    (python311.withPackages (ps: with ps; [
      (ps.opencv4.override {
        enableGtk3 = true;
      })
  ];
}

It will take some time to build depending on your hardware.

While we are here, if you plan to use mediapipe (not packaged for Nix yet), then you’ll need the following:

{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  name = "opencv-python-gtk";
  buildInputs = with pkgs; [
    (python311.withPackages (ps: with ps; [
      (ps.opencv4.override {
        enableGtk3 = true;
    })

    zlib
    libGL
    glib
  ];

  LD_LIBRARY_PATH = "${pin.pkgs.zlib}/lib:${pin.pkgs.stdenv.cc.cc.lib}/lib:${pin.pkgs.libGL}/lib:${pin.pkgs.glib.out}/lib:/run/opengl-driver/lib";
}

Finally, since opencv will be rebuilt everytime you update your channels, you can pin it as follows:

{}:

let
  pin = rec {
    commit = "72c6ed328aa4e5d9151b1a512f6ad83aca7529fa"; # nixos-unstable @ 2023-03-28
    pkgsSrc = builtins.fetchTarball {
      url = "https://github.com/NixOS/nixpkgs/archive/${commit}.tar.gz";
      # Set to empty and wait for the error to tell you the right one
      sha256 = "1fzrqm29n6iq1c998ym5ijsj5x3z1l07qkc4xb48y9c22bl8cn11";
    };
    pkgs = import pkgsSrc {};
  };

in

pin.pkgs.mkShell {
  name = "opencv-python-gtk";
  buildInputs = with pin.pkgs; [
    (python311.withPackages (ps: with ps; [
      (ps.opencv4.override {
        enableGtk3 = true;
    })

    zlib
    libGL
    glib
  ];

  LD_LIBRARY_PATH = "${pin.pkgs.zlib}/lib:${pin.pkgs.stdenv.cc.cc.lib}/lib:${pin.pkgs.libGL}/lib:${pin.pkgs.glib.out}/lib:/run/opengl-driver/lib";
}

This way your opencv will built only once until you explicitly will want to upgrade.