Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabling OpenEXR after python module import #24470

Open
martinResearch opened this issue Oct 30, 2023 · 10 comments
Open

Enabling OpenEXR after python module import #24470

martinResearch opened this issue Oct 30, 2023 · 10 comments
Labels

Comments

@martinResearch
Copy link

Describe the feature and motivation

Problem
The current method to enable OpenEXR image reading in python is to add a line with os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" before the import of opencv using import cv2.

This goes against the pep8 guideline that "Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants." (see https://peps.python.org/pep-0008/#imports).
As a consequence linters such as ruff, pycodestyle or flake8 generate errors .
One can add comments to ignore the specific linting error in the lines following the line that contains os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" but this make the linting weaker and pollutes the code with comments to ignore the errors.

Feature
It would be great if we could import opencv first using import cv2 and then enable OpenEXR within the code following the imports section using a method cv2.enable_openexr() for example.

Additional context

No response

@asmorkalov
Copy link
Contributor

In general: OpenCV uses environment variables only and there is no alternative to mentioned code right now.
In particular: OPENCV_IO_ENABLE_OPENEXR default value may be set in compile time. You need to add -DOPENCV_IO_FORCE_OPENEXR=ON to cmake options. Details:

@martinResearch
Copy link
Author

" You need to add -DOPENCV_IO_FORCE_OPENEXR=ON to cmake options." that is a valid option when using opencv in C++, however I am looking for a solution that can be used when using the official wheel from opencv-python package without having to change the OPENCV_IO_ENABLE_OPENEXR default value and thus without having to recompile the wheel.
I was hopping there would be a way to add a feature that allows the user to tell the opencv package that the value of the environment variable OPENCV_IO_ENABLE_OPENEXR has been changed after the module has been loaded in python without changing it default value. It could be a cv.reload_environment_variables() method for example.

@Kumataro
Copy link
Contributor

Kumataro commented Nov 10, 2023

Hello, I'm very sorry but I think this issue's description is not correct exactlly.

The current method to enable OpenEXR image reading in python is
to add a line with os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1"
- before the import of opencv using import cv2.
+ before first calling imread/imwrite family functions. 

For example, following code works well. Please could you try it ?

import os
import cv2
os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" # it works well.
src = cv2.imread("test32FC3.exr" ) # OK
os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "0" # it means nothing.
src = cv2.imread("test32FC3.exr" ) # OK

It seems that this envitonment variable is evaluated when calling imread/imwrite family functions, not loading opencv library.
(Following is about reading, but writing is same as it.).

(gdb) bt
#0  cv::isOpenEXREnabled () at /home/kmtr/work/opencv/modules/imgcodecs/src/grfmt_exr.cpp:87
#1  0x00007ffff6c4a272 in cv::initOpenEXR () at /home/kmtr/work/opencv/modules/imgcodecs/src/grfmt_exr.cpp:99
#2  0x00007ffff6c4d948 in cv::ExrDecoder::newDecoder (this=0x555555570220)
    at /home/kmtr/work/opencv/modules/imgcodecs/src/grfmt_exr.cpp:604
#3  0x00007ffff6c246a2 in cv::findDecoder (filename="test32FC3.exr")
    at /home/kmtr/work/opencv/modules/imgcodecs/src/loadsave.cpp:262
#4  0x00007ffff6c2555a in cv::imread_ (filename="test32FC3.exr", flags=1, mat=...)
    at /home/kmtr/work/opencv/modules/imgcodecs/src/loadsave.cpp:405
#5  0x00007ffff6c26bb3 in cv::imread (filename="test32FC3.exr", flags=1)
    at /home/kmtr/work/opencv/modules/imgcodecs/src/loadsave.cpp:636
#6  0x0000555555556413 in readTest() ()
#7  0x0000555555556574 in main ()

@martinResearch
Copy link
Author

I am getting a different behavior:

import os
import cv2  # '4.8.1'
import numpy as np

os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1"

cv2.imwrite("test.exr", np.random.rand(10, 10))

gives me an error OpenCV(4.8.1) D:\a\opencv-python\opencv-python\opencv\modules\imgcodecs\src\grfmt_exr.cpp:103: error: (-213:The function/feature is not implemented) imgcodecs: OpenEXR codec is disabled. You can enable it via 'OPENCV_IO_ENABLE_OPENEXR' option. Refer for details and cautions here: https://github.com/opencv/opencv/issues/21326 in function 'cv::initOpenEXR'

I am executing this in Windows using python opencv 4.8.1. Maybe the problem appears only on Windows?

@Kumataro
Copy link
Contributor

I'll investigate a little more...

Maybe the problem appears only on Windows?

I try with Ubuntu 23.10, but it was not be confirmed. And after encoder is created, other error is happened. It is a little strange...

import os
import cv2  # '4.8.1'
import numpy as np

os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1"
cv2.imwrite("test.exr", np.random.rand(10,10) )

np.random.rand(10,10) returns 10x10 matrix contains real number from 0.0 to 1.0. I think it is correct code.

kmtr@kmtr-None:~/work/build4-main_python3.10/exr$ python3 test.py
Traceback (most recent call last):
  File "/home/kmtr/work/build4-main_python3.10/exr/test.py", line 6, in <module>
    cv2.imwrite("test.exr", np.random.rand(10,10) )
cv2.error: OpenCV(4.8.1) /home/kmtr/work/opencv/modules/imgcodecs/src/loadsave.cpp:708: error: (-215:Assertion failed) encoder->isFormatSupported(CV_8U) in function 'imwrite_'

kmtr@kmtr-None:~/work/build4-main_python3.10/exr$ python3 --version
Python 3.10.8
kmtr@kmtr-None:~/work/build4-main_python3.10/exr$ uname -a
Linux kmtr-None 6.5.0-10-generic #10-Ubuntu SMP PREEMPT_DYNAMIC Fri Oct 13 13:49:38 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

I think it should be return true this function. (it may be another python binding problem.)

bool  ExrEncoder::isFormatSupported( int depth ) const
{
    return ( CV_MAT_DEPTH(depth) == CV_32F );
}

@Kumataro
Copy link
Contributor

( I'm sorry I am not Visual Studio friendly. )

I tried it with Visual Studio 2022. However same exception has occurred from imread() and imwrite() function.
I worried that MSVC works different rules with GCC, but it is not correct.
It is same as ubuntu result, so I think same error should be happen in the same timing.

image

image

@Kumataro
Copy link
Contributor

(If it is not correct, I'm very sorry), I think the conclusion is that dynamic support does not work perfectly with the current code for windows. Linux is OK.

Ref. python/cpython#60837 (comment).

I think...

  1. import os
    • os.environt[] is created for each process from system environment variable.
  2. import cv2
  3. os.environt["OPENCV_IO_ENABLE_OPENEXR"]="1"
    • [Both] it is changed directly, so it causes python-world.
    • [Linux] it calls putenv(), and this change effetcs C-API world too.
    • [Windows] it calls putenv(), and this change effetcs Win32 world, not C-API world
  4. When user problem calls imread() or imwrite(), ExrEncoder/Decoder reference environment variable at C-API world .

In order to enable OpenEXR on Windows, new criteria that do not use environment variables are required. For example, it is based on the function you suggested (cv2.enable_openexr()).

I feel It is need to comment from others.

@asmorkalov
Copy link
Contributor

It's know issue on Windows. Windows makes own copy of environment variables for DLL, but does not share them with host app. That's why the setting works before import and does not after. See related discussion on stackoverflow: https://stackoverflow.com/questions/5153547/environment-variables-are-different-for-dll-than-exe. We can do nothing with it, rather than re-implement settings in OpenCV in general.

@martinResearch
Copy link
Author

Maybe an option would be to use win32's getenvironmentstrings in here in a #ifndef _WIN32_WINNT section and replace direct calls to getenv by calls to envRead everywhere else in the code.

@martinResearch
Copy link
Author

For those interested, I created a small python package openexr-numpy around official OpenEXR python package to help reading and writing exr file to and from numpy arrays without opencv. This can be used as a workaround to avoid having to add the environment variable OPENCV_IO_ENABLE_OPENEXR before any opencv import on windows. It also provide a more direct access to some features supported by the exr format, like arbitrary number of channels and channels with different types for example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants