From 22efe7319b0544da7a6285da068c90a919b81283 Mon Sep 17 00:00:00 2001 From: Luflosi Date: Thu, 10 Aug 2023 17:50:57 +0200 Subject: [PATCH 1/2] python3Packages.invisible-watermark: 0.1.5 -> 0.2.0 --- .../invisible-watermark/default.nix | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pkgs/development/python-modules/invisible-watermark/default.nix b/pkgs/development/python-modules/invisible-watermark/default.nix index 3c48dcc4d60b..b131cfa54996 100644 --- a/pkgs/development/python-modules/invisible-watermark/default.nix +++ b/pkgs/development/python-modules/invisible-watermark/default.nix @@ -9,34 +9,39 @@ , pillow , pywavelets , numpy +, withOnnx ? false # Enables the rivaGan en- and decoding method }: buildPythonPackage rec { pname = "invisible-watermark"; - version = "0.1.5"; + version = "0.2.0"; format = "setuptools"; disabled = pythonOlder "3.6"; src = fetchFromGitHub { owner = "ShieldMnt"; repo = "invisible-watermark"; - rev = version; - hash = "sha256-NGDPEETuM7rYbo8kXYoRWLJWpa/lWLKEvaaiDzSWYZ4="; + rev = "e58e451cff7e092457cd915e445b1a20b64a7c8f"; # No git tag, see https://github.com/ShieldMnt/invisible-watermark/issues/22 + hash = "sha256-6SjVpKFtiiLLU7tZ3hBQr0KT/YEQyywJj0e21/dJRzk="; }; propagatedBuildInputs = [ opencv4 torch - onnx - onnxruntime pillow pywavelets numpy + ] ++ lib.optionals withOnnx [ + onnx + onnxruntime ]; postPatch = '' substituteInPlace setup.py \ --replace 'opencv-python>=4.1.0.25' 'opencv' + substituteInPlace imwatermark/rivaGan.py --replace \ + 'You can install it with pip: `pip install onnxruntime`.' \ + 'You can install it with an override: `python3Packages.invisible-watermark.override { withOnnx = true; };`.' ''; pythonImportsCheck = [ "imwatermark" ]; From 04120a39656d7a278a5e52d58ab879f1fcea539a Mon Sep 17 00:00:00 2001 From: Luflosi Date: Tue, 10 Oct 2023 13:25:03 +0200 Subject: [PATCH 2/2] python3Packages.invisible-watermark: add tests After the 0.2.0 version, there are even more possible cases to consider. I found it too annoying to do all the testing manually. Add some tests to make it easy to test everything automatically. The test python3Packages.invisible-watermark.tests.withOnnx-rivaGan would fail in the nix sandbox on aarch64-linux: ``` Error in cpuinfo: failed to parse the list of possible processors in /sys/devices/system/cpu/possible Error in cpuinfo: failed to parse the list of present processors in /sys/devices/system/cpu/present Error in cpuinfo: failed to parse both lists of possible and present processors terminate called after throwing an instance of 'onnxruntime::OnnxRuntimeException' what(): /build/source/include/onnxruntime/core/common/logging/logging.h:294 static const onnxruntime::logging::Logger& onnxruntime::logging::LoggingManager::DefaultLogger() Attempt to use DefaultLogger but none has been registered. /build/.attr-0l2nkwhif96f51f4amnlf414lhl4rv9vh8iffyp431v6s28gsr90: line 9: 5 Aborted (core dumped) invisible-watermark --verbose --action encode --type bytes --method 'rivaGan' --watermark 'asdf' --output output.png '/nix/store/srl698a32n9d2pmyf5zqfk65gjzq3mhp-source/test_vectors/original.jpg' Exit code of invisible-watermark was 134 while 0 was expected. ``` so I have disabled that test. I believe https://github.com/microsoft/onnxruntime/issues/10038 describes the same issue. --- .../invisible-watermark/default.nix | 21 ++++++ .../invisible-watermark/tests/cli.nix | 64 +++++++++++++++++++ .../tests/python/decode.py | 19 ++++++ .../tests/python/default.nix | 42 ++++++++++++ .../tests/python/encode.py | 21 ++++++ 5 files changed, 167 insertions(+) create mode 100644 pkgs/development/python-modules/invisible-watermark/tests/cli.nix create mode 100644 pkgs/development/python-modules/invisible-watermark/tests/python/decode.py create mode 100644 pkgs/development/python-modules/invisible-watermark/tests/python/default.nix create mode 100644 pkgs/development/python-modules/invisible-watermark/tests/python/encode.py diff --git a/pkgs/development/python-modules/invisible-watermark/default.nix b/pkgs/development/python-modules/invisible-watermark/default.nix index b131cfa54996..eab0c1c360aa 100644 --- a/pkgs/development/python-modules/invisible-watermark/default.nix +++ b/pkgs/development/python-modules/invisible-watermark/default.nix @@ -1,4 +1,5 @@ { lib +, stdenv , buildPythonPackage , pythonOlder , fetchFromGitHub @@ -9,6 +10,7 @@ , pillow , pywavelets , numpy +, callPackage , withOnnx ? false # Enables the rivaGan en- and decoding method }: @@ -44,6 +46,25 @@ buildPythonPackage rec { 'You can install it with an override: `python3Packages.invisible-watermark.override { withOnnx = true; };`.' ''; + passthru.tests = let + image = "${src}/test_vectors/original.jpg"; + methods = [ "dwtDct" "dwtDctSvd" "rivaGan" ]; + testCases = builtins.concatMap (method: [ + { method = method; withOnnx = true; } + { method = method; withOnnx = false; } + ]) methods; + createTest = { method, withOnnx }: let + testName = "${if withOnnx then "withOnnx" else "withoutOnnx"}-${method}"; + # This test fails in the sandbox on aarch64-linux, see https://github.com/microsoft/onnxruntime/issues/10038 + skipTest = stdenv.isLinux && stdenv.isAarch64 && withOnnx && method == "rivaGan"; + in lib.optionalAttrs (!skipTest) { + "${testName}" = callPackage ./tests/cli.nix { inherit image method testName withOnnx; }; + }; + allTests = builtins.map createTest testCases; + in (lib.attrsets.mergeAttrsList allTests) // { + python = callPackage ./tests/python { inherit image; }; + }; + pythonImportsCheck = [ "imwatermark" ]; meta = with lib; { diff --git a/pkgs/development/python-modules/invisible-watermark/tests/cli.nix b/pkgs/development/python-modules/invisible-watermark/tests/cli.nix new file mode 100644 index 000000000000..9cb936465786 --- /dev/null +++ b/pkgs/development/python-modules/invisible-watermark/tests/cli.nix @@ -0,0 +1,64 @@ +{ image +, method +, python3Packages +, runCommand +, testName +, withOnnx +}: + +# This file runs one test case. +# There are six test cases in total. method can have three possible values and +# withOnnx two possible values. 3 * 2 = 6. +# +# The case where the method is rivaGan and invisible-watermark is built +# without onnx is expected to fail and this case is handled accordingly. +# +# The test works by first encoding a message into a test image, +# then decoding the message from the image again and checking +# if the message was decoded correctly. + +let + message = if method == "rivaGan" then + "asdf" # rivaGan only supports 32 bits + else + "fnörd1"; + length = (builtins.stringLength message) * 8; + invisible-watermark' = python3Packages.invisible-watermark.override { inherit withOnnx; }; + expected-exit-code = if method == "rivaGan" && !withOnnx then "1" else "0"; +in +runCommand "invisible-watermark-test-${testName}" { nativeBuildInputs = [ invisible-watermark' ]; } '' + set +e + invisible-watermark \ + --verbose \ + --action encode \ + --type bytes \ + --method '${method}' \ + --watermark '${message}' \ + --output output.png \ + '${image}' + exit_code="$?" + set -euf -o pipefail + if [ "$exit_code" != '${expected-exit-code}' ]; then + echo "Exit code of invisible-watermark was $exit_code while ${expected-exit-code} was expected." + exit 1 + fi + if [ '${expected-exit-code}' == '1' ]; then + echo 'invisible-watermark failed as expected.' + touch "$out" + exit 0 + fi + decoded_message="$(invisible-watermark \ + --action decode \ + --type bytes \ + --method '${method}' \ + --length '${toString length}' \ + output.png \ + )" + + if [ '${message}' != "$decoded_message" ]; then + echo "invisible-watermark did not decode the watermark correctly." + echo "The original message was ${message} but the decoded message was $decoded_message." + exit 1 + fi + touch "$out" +'' diff --git a/pkgs/development/python-modules/invisible-watermark/tests/python/decode.py b/pkgs/development/python-modules/invisible-watermark/tests/python/decode.py new file mode 100644 index 000000000000..c1b7e52ff6f5 --- /dev/null +++ b/pkgs/development/python-modules/invisible-watermark/tests/python/decode.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +import os +import cv2 +from imwatermark import WatermarkDecoder + +input_file = os.environ['image'] +output_file_path = os.environ['out'] +num_bits = int(os.environ['num_bits']) +method = os.environ['method'] + +bgr = cv2.imread(input_file) + +decoder = WatermarkDecoder('bytes', num_bits) +watermark = decoder.decode(bgr, method) +message = watermark.decode('utf-8') + +with open(output_file_path, 'w') as f: + f.write(message) diff --git a/pkgs/development/python-modules/invisible-watermark/tests/python/default.nix b/pkgs/development/python-modules/invisible-watermark/tests/python/default.nix new file mode 100644 index 000000000000..63db58867371 --- /dev/null +++ b/pkgs/development/python-modules/invisible-watermark/tests/python/default.nix @@ -0,0 +1,42 @@ +{ image +, invisible-watermark +, opencv4 +, python3 +, runCommand +, stdenvNoCC +}: + +# This test checks if the python code shown in the README works correctly + +let + message = "fnörd1"; + method = "dwtDct"; + + pythonWithPackages = python3.withPackages (pp: with pp; [ invisible-watermark opencv4 ]); + pythonInterpreter = pythonWithPackages.interpreter; + + encode = stdenvNoCC.mkDerivation { + name = "encode"; + realBuilder = pythonInterpreter; + args = [ ./encode.py ]; + inherit image message method; + }; + + decode = stdenvNoCC.mkDerivation { + name = "decode"; + realBuilder = pythonInterpreter; + args = [ ./decode.py ]; + inherit method; + image = "${encode}/test_wm.png"; + num_bits = (builtins.stringLength message) * 8; + }; +in +runCommand "invisible-watermark-test-python" { } '' + decoded_message="$(cat '${decode}')" + if [ '${message}' != "$decoded_message" ]; then + echo "invisible-watermark did not decode the watermark correctly." + echo "The original message was ${message} but the decoded message was $decoded_message." + exit 1 + fi + touch "$out" +'' diff --git a/pkgs/development/python-modules/invisible-watermark/tests/python/encode.py b/pkgs/development/python-modules/invisible-watermark/tests/python/encode.py new file mode 100644 index 000000000000..59a72cffe70d --- /dev/null +++ b/pkgs/development/python-modules/invisible-watermark/tests/python/encode.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +import os +import cv2 +from imwatermark import WatermarkEncoder + +input_file_path = os.environ['image'] +output_dir = os.environ['out'] +message = os.environ['message'] +method = os.environ['method'] + +os.mkdir(output_dir) + +bgr = cv2.imread(input_file_path) + +encoder = WatermarkEncoder() +encoder.set_watermark('bytes', message.encode('utf-8')) +bgr_encoded = encoder.encode(bgr, method) + +output_file = os.path.join(output_dir, 'test_wm.png') +cv2.imwrite(output_file, bgr_encoded)