[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.

1 Like
{ }:

let
  # 1) Define an overlay that really touches pkgs.opencv4
  myOverlay = final: prev: {
    opencv4 = prev.opencv4.overrideAttrs (oldAttrs: rec {
      # flip on the CUDA bits that the stock recipe left off
      enableCuda   = true;
      enableCudnn  = true;
      enableCublas = true;
      enableCufft  = true;

      # pull in the toolkit & libs the CMakeLists will look for
      buildInputs = oldAttrs.buildInputs ++ [
        final.cudaPackages.cudatoolkit
        final.cudaPackages.cudnn
        final.cudaPackages.libcublas
        final.cudaPackages.libcufft
        final.libGLU
        final.libGL
        final.xorg.libX11
        final.xorg.libXi
        final.xorg.libXmu
      ];

      # extend exactly the same `cmakeFlags` you saw in the log
      cmakeFlags = oldAttrs.cmakeFlags ++ [
        "-DCMAKE_POLICY_DEFAULT_CMP0146=NEW"
        "-DCUDA_TOOLKIT_ROOT_DIR=${final.cudaPackages.cudatoolkit}"
        "-DCUDA_ARCH_BIN=8.6"
        "-DWITH_CUDA=ON"
        "-WITH_NVCUVID=ON"
        "-DWITH_CUDNN=ON"
        "-DOPENCV_DNN_CUDA=ON"
        "-DCUDA_FAST_MATH=ON"
        "-DWITH_CUBLAS=ON"
        "-DWITH_CUFFT=ON"
        "-DBUILD_opencv_cudacodec=ON"
        "-DOPENCV_ENABLE_NONFREE=ON"
        "-DCUDA_NVCC_FLAGS=--expt-relaxed-constexpr"
        "-DCMAKE_VERBOSE_MAKEFILE=ON"
      ];

      # ensure the build really sees your CUDA env
      preConfigure = (oldAttrs.preConfigure or "") + ''
        export CUDA_PATH=${final.cudaPackages.cudatoolkit}
        export CUDA_HOME=${final.cudaPackages.cudatoolkit}
        export CUDNN_HOME=${final.cudaPackages.cudnn}
        export NVCCFLAGS="-gencode arch=compute_86,code=sm_86"
      '';
    });
  };

  # 2) Import nixpkgs *with* that overlay
  pkgs = import <nixpkgs> {
    config.allowUnfree = true;
    overlays = [ myOverlay ];
  };

  # 3) Your Python env (now pulling in the new CUDA-built opencv4)
  pythonEnv = pkgs.python312.withPackages (ps: with ps; [
    numpy mysql-connector setuptools flask flask-cors pymongo deepface yt-dlp wheel dlib
    opencv4
  ]);
in

# 4) Use mkShell for a dev shell
pkgs.mkShell {
  name = "cvCuda_test";

  buildInputs = [
    pkgs.cudaPackages.cudatoolkit
    pkgs.linuxPackages.nvidia_x11
    pkgs.opencv4      # <— this is now your CUDA-enabled OpenCV!
    pythonEnv         # and your Python with cv2 bound to that build
  ];

  shellHook = ''
    export PATH=${pythonEnv}/bin:$PATH
    echo "🔮 Welcome to Quartzite Dev Shell!"
    echo "CUDA Opencv4 → $(python3 -c 'import cv2; print(cv2.getBuildInformation().splitlines()[0])')"
    python3 - <<<'import cv2; print("CUDA devices:", cv2.cuda.getCudaEnabledDeviceCount())'
  '';
}

For posterity this is what I had to do as of 2025