lilohuang / pyturbojpeg Goto Github PK
View Code? Open in Web Editor NEWPyTurboJPEG is a highly optimized Python wrapper of libjpeg-turbo (TurboJPEG API) which supports x86 and ARM architecture.
License: MIT License
PyTurboJPEG is a highly optimized Python wrapper of libjpeg-turbo (TurboJPEG API) which supports x86 and ARM architecture.
License: MIT License
Hi,
There seems to be a case of unmanaged memory in PyTurboJPEG. Memory is allocated during the image decode process, but not deallocated later (at least not immediately), and the memory usage of the process keeps building up.
As an example, please consider the following code:
from turbojpeg import TurboJPEG, TJPF_GRAY, TJSAMP_GRAY
import psutil
import os
import time
jpeg = TurboJPEG("/usr/lib/x86_64-linux-gnu/libturbojpeg.so.0")
import numpy
i = 0
PROCESS = psutil.Process(os.getpid())
buf = None
with open('some_image.jpg', 'rb') as fin:
buf = fin.read()
for i in range(100):
print 'iteration', i
img_array = jpeg.decode(buf)
print img_array.shape
print 'memory = ', PROCESS.memory_info().rss // 1024
The output of this program looks like this:
...
iteration 43
(2160, 4096, 3)
memory = 806464
iteration 44
(2160, 4096, 3)
memory = 832576
iteration 45
(2160, 4096, 3)
memory = 858472
iteration 46
(2160, 4096, 3)
memory = 884340
iteration 47
(2160, 4096, 3)
memory = 910188
iteration 48
(2160, 4096, 3)
memory = 936316
iteration 49
(2160, 4096, 3)
memory = 962216
iteration 50
...
The memory usage keeps building up till more than a GB. It sometimes goes back to a low value, but again the buildup happens.
The other popular turbojpeg python wrapper -- jpeg4py -- doesn't have this problem. However, I prefer using PyTurboJPEG because it offers a scaling factor in the decode function which speeds up things considerably for my use case. But the memory build up is causing problems as I have a tight memory budget.
What could be the reason and are there any known fixes/workarounds?
libjpeg-turbo supports partial decoding, but that isn't exposed yet in PyTurboJPEG. it would be great if the decode function supported x, y, desiredWidth, and desiredHeight much like the java API does.
I did brew install jpeg-turbo
And when I try to do
from turbojpeg import TurboJPEG
jpeg = TurboJPEG()
I get the following error
OSError: dlopen(/opt/homebrew/opt/jpeg-turbo/lib/libturbojpeg.dylib, 0x0006): tried: '/opt/homebrew/opt/jpeg-turbo/lib/libturbojpeg.dylib' (mach-o file, but is an incompatible architecture (have (arm64), need (x86_64))), '/opt/homebrew/Cellar/jpeg-turbo/2.1.4/lib/libturbojpeg.0.2.0.dylib' (mach-o file, but is an incompatible architecture (have (arm64), need (x86_64)))
I think this is because I used x86_64 for python installation. Any way to get around this problem? It works on my default python3.9 which came with the mac.
I pip3 install PyTurboJPEG
. When I run an open-source openpose
code, I encounter this issue. Anyone facing this similar issue?
Hello! Thanks for offering this repository!
Due to my work, I need to get encoded data from BGR image without marker and then decode into BGR image. How can I realize it?
The current situation is that the pkgsrc package libjpeg-turbo-2.1.2 installs
-rw-r--r-- 1 root wheel 427360 Dec 25 16:28 /usr/pkg/lib/libturbojpeg.a
lrwxr-xr-x 1 root wheel 17 Dec 25 16:31 /usr/pkg/lib/libturbojpeg.so -> libturbojpeg.so.0
lrwxr-xr-x 1 root wheel 21 Dec 25 16:31 /usr/pkg/lib/libturbojpeg.so.0 -> libturbojpeg.so.0.2.0
-rwxr-xr-x 1 root wheel 345916 Dec 25 16:25 /usr/pkg/lib/libturbojpeg.so.0.2.0
so it should be sufficient to copy the FreeBSD block with /usr/local changed to /usr/pkg.
Hey there,
still one more hint: I believe it's a cleaner interface if you'd use from ctypes.util import find_library
like find_library("turbojpeg")
to detect the path to the library instead of the hardcoded dictionary :) (I'm doing exactly that and pass the result to the constructor).
Thanks!
Hi,
I wish know in what image array format does the encode function accept? BGR of RGB?
I aim to use opencv to read a video frame and encode is with TurboJPEG.encode(). OpenCV gives me a BGR array. Should convert it to RGB or should I pass it to encode as it is?
Hello,
I have a feature request: A method to decode the header only would be great :)
E.g.:
def decode_header(self, jpeg_buf):
"""decode JPEG memory buffer header and return a tuple (width, height ."""
handle = self.__init_decompress()
try:
if scaling_factor is not None and \
scaling_factor not in self.__scaling_factors:
raise ValueError('supported scaling factors are ' +
str(self.__scaling_factors))
pixel_size = [3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4, 4]
width = c_int()
height = c_int()
jpeg_subsample = c_int()
jpeg_colorspace = c_int()
jpeg_array = np.frombuffer(jpeg_buf, dtype=np.uint8)
src_addr = jpeg_array.ctypes.data_as(POINTER(c_ubyte))
status = self.__decompress_header(
handle, src_addr, jpeg_array.size, byref(width), byref(height),
byref(jpeg_subsample), byref(jpeg_colorspace))
if status != 0:
raise IOError(self.__get_error_str().decode())
return (width.value, height.value, jpeg_subsample.value, jpeg_colorspace.value)
finally:
self.__destroy(handle)
Thank you!
Hello! Thanks for your lib, it's a very useful tool for fast work with JPEG format.
Can you please tell me how can i pass the color profile (like icc_profile in Pillow) for picture's encode? If this feature is not available, can it be added during the next release?
On Apple silicon, Homebrew installs under /opt/homebrew
instead of /usr/local
.
Consequently, brew install jpeg-turbo
results in the lib path "/opt/homebrew/opt/jpeg-turbo/lib/libturbojpeg.dylib"
.
Would it please be possible to check for this path as well on OSX when calling the default constructor TurboJPEG()
?
As of now the user has to dig into the brew install to figure out the right path to pass to the TurboJPEG
constructor.
The error that I get is:
/opt/libjpeg-turbo/lib64/libjpeg.so: undefined symbol: tjInitDecompress
How to reproduce:
Download the .deb for 2.0.4 from https://sourceforge.net/projects/libjpeg-turbo/ and install using dpkg
Then, from python
>>> from turbojpeg import TurboJPEG, TJPF_GRAY, TJSAMP_GRAY
>>> jpeg = TurboJPEG("/opt/libjpeg-turbo/lib64/libjpeg.so")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/test/.local/lib/python2.7/site-packages/turbojpeg.py", line 98, in __init__
self.__init_decompress = turbo_jpeg.tjInitDecompress
File "/usr/lib/python2.7/ctypes/__init__.py", line 375, in __getattr__
func = self.__getitem__(name)
File "/usr/lib/python2.7/ctypes/__init__.py", line 380, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /opt/libjpeg-turbo/lib64/libjpeg.so: undefined symbol: tjInitDecompress
I checked and indeed the symbol tjInitDecompress isn't defined in the new .so file.
Version 2.0.3 didn't have this problem (the symbol is defined in the .so for 2.0.3)
Hi @lilohuang, I've pip-installed TurboJPEG, numpy and PyTurboJPEG, and yet when I ran the sample code provided in the readme file, python keeps returning error message saying that TurboJPEG() can't be found. I'm not sure if it's due to my lack of knowledge of python or maybe because of certain environment variables, but i do have turbojpeg64/bin/ (where turbojpeg.dll is) path stored in the path environment variable in my Windows machine. Am I missing something? I'm running Windows 11 with Python 3.11, MS VC++ 2015-2022 Redist and VS Code
I've done research and somehow couldn't find anything that helps with resolving this issue. I've also attempted to use turbojpeg-gcc64 and replaced the previous turbojpeg64\bin path environment to gcc64\bin one. Yet, issue persists.
I've also tried to implement and use my own custom class, or used a different module's class. I don't have any problem with this one. I'm thinking maybe because I'm using Python 3.11? I have yet to test this out though.
Followed the installation instruction on Mac 11.0.1.
/usr/local/lib/libturbojpeg.dylib
does not existjpeg = TurboJPEG()
throws exceptionHi and thank you for your work on this great package.
I'm trying to save a simple 2D numpy
array into a grayscale image, using this test code:
import numpy as np
from turbojpeg import TJPF_GRAY, TurboJPEG
jpeg = TurboJPEG()
rng = np.random.default_rng()
image = rng.integers(low=0, high=255, dtype=np.uint8, size=(3008, 4112))
with open("test.jpg", "wb") as output_jpeg_file:
output_jpeg_file.write(jpeg.encode(img_array=image, pixel_format=TJPF_GRAY))
and what I get is the following error (obviously, because the input array has only 2 dimensions):
height, width, _ = img_array.shape
ValueError: not enough values to unpack (expected 3, got 2)
I would expect this code to work, since if I pass pixel_format=TJPF_GRAY
a 2D array should suffice to give all the necessary data to the encoder. Even if I try to augment the dimensions yielding three identical channels (using np.dstack
), I get a OSError: Unsupported color conversion request
error on the encode
command.
What am I doing wrong? What can be done to obtain a grayscale JPEG?
I admire the rich functionality of the project, but is there really no way to read JPG into DCT coefficients?
I train forgery detecting NN and it uses several DCT features. It would be great if I can use PyTurboJPEG for image loading right into them, bypassing decoding to YUV and making DCT again.
how to decode png
I was debugging why my mjpg_streamer
images wouldn't show in Home Assistant's preview cards. Turns out HA is using PyTurboJPEG to try to rescale down the source image to a better fit to the card size, but my camera is producing some flaky (m)jpeg output that triggers these warnings:
/srv/homeassistant/lib/python3.9/site-packages/turbojpeg.py:867: UserWarning: Corrupt JPEG data: 1 extraneous bytes before marker 0xd4
It turns out the return
in this line needs to be removed:
if status != 0:
self.__report_error(handle)
return
__report_error()
will already raise an exception when an actual error has occurred, so no return is necessary. When what we had was a warning only, the return
will prevent the code from proceeding.
I tested this by modifying my own installation to remove the line and see that I now have this camera's preview in the HA card.
Hi,
any chance you could add lossless drop?
https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/transupp.h
thank you,
Tomaz
I am installing the package in Windows. Following the instruction, I did the following two steps:
libjpeg-turbo-2.0.3-gcc.exe
). After the installation, I saw it is installed in C:/libjpeg-turbo-gcc
.$ pip install -U git+git://github.com/lilohuang/PyTurboJPEG.git
After the above installation steps, I encountered the following error when running the sample script:
>>> from turbojpeg import TurboJPEG
>>> x = TurboJPEG()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\u3509\Anaconda3\envs\realsense\lib\site-packages\turbojpeg.py", line 97, in __init__
self.__find_turbojpeg() if lib_path is None else lib_path)
File "C:\Users\u3509\Anaconda3\envs\realsense\lib\site-packages\turbojpeg.py", line 257, in __find_turbojpeg
'Unable to locate turbojpeg library automatically. '
RuntimeError: Unable to locate turbojpeg library automatically. You may specify the turbojpeg library path manually.
e.g. jpeg = TurboJPEG(lib_path)
I noticed I may need to specify library path explicitly, but I can't find the target files (i.e. turbojpeg.dll
, libturbojpeg.so
, or libturbojpeg.so
) in my libjpeg-turbo directory (i.e. C:/libjpeg-turbo-gcc
)
Below is what I saw from C:\libjpeg-turbo-gcc\lib\
(base) PS C:\Users\u3509> ls C:\libjpeg-turbo-gcc\lib\
Directory: C:\libjpeg-turbo-gcc\lib
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/20/2019 8:55 PM pkgconfig
-a---- 9/5/2019 7:38 AM 772182 libjpeg.a
-a---- 9/5/2019 7:35 AM 70746 libjpeg.dll.a
-a---- 9/5/2019 7:36 AM 878050 libturbojpeg.a
-a---- 9/5/2019 7:37 AM 52760 libturbojpeg.dll.a
Hi @lilohuang,
I was using turboJPEG with python 2.7 and it was working fine. I was using
https://downloads.sourceforge.net/project/libjpeg-turbo/2.0.2/libjpeg-turbo-official_2.0.2_amd64.deb
and your latest code repo.
But when i migrated to python 3.7, the same fucntions which was working before is creating some exceptions. Please see the exceptions stack trace below.
Traceback (most recent call last):
File "/opt/code/faceid/utils.py", line 86, in convert_img_to_buffer
jpg_as_text = turbo.encode(frame, quality=95)
File "/usr/local/lib/python3.7/site-packages/turbojpeg.py", line 219, in encode
self.__report_error(handle)
File "/usr/local/lib/python3.7/site-packages/turbojpeg.py", line 235, in __report_error
raise IOError(self.__get_error_string(handle))
OSError: tjCompress2(): Invalid argument
I am unable to understand what is the issue here. Can you please point me.
Thanks,
Srijith
If I try to encode an image with missing components, the python interpreter silently exits with no message/exception.
See this example code:
from numpy import uint8, zeros
from turbojpeg import TJPF_RGB, TJSAMP_420, TurboJPEG
jpeg = TurboJPEG()
image = zeros((1024, 4096), dtype=uint8)
print("Entering store...")
with open("test.jpg", "wb") as f:
jpeg_encoded = jpeg.encode(
image,
pixel_format=TJPF_RGB,
quality=95,
jpeg_subsample=TJSAMP_420,
)
f.write(jpeg_encoded)
print("Exiting store (this never gets printed)...")
Conversely, if I say image = zeros((1024, 4096, 3), dtype=uint8)
, the script completes normally.
Issue appears with both pyturbojpeg
1.6.3 and 1.6.4, on Windows 10x64.
I would expect to see an exception raised in this case.
Thank you for your work on this great wrapper library, and happy new year!
Hi,
I have cropped an image part from an open cv frame. Then i encode it , decode it and write it as a file gives a distorted image.
Encoding
---------
face_crop = frame[bbox[1]:bbox[3], bbox[0]:bbox[2], :]
jpg_as_text = turbo.encode(face_crop)
Decoding
------------
img_file = io.BytesIO(jpg_as_text)
face_crop_decoded = turbo.decode(img_file.read())
Now if i write face_crop_decoded using cv2.imwrite('face.jpg', face_crop_decoded) , distorted lines are coming , not the actual face.
But if i use original face_crop for cv2.imwrite , proper face is getting written. So encode-decode-write causing some issues. But the frame variable which is used in the first line is actually encoded-decoded, there i am able to encode and decode properly. But on face_crop encode-decode is not working. What would be the issue here ?
Thank You
Hey there,
I have another request: Some specific images yield warnings, which are currently handled as exceptions (see libjpeg-turbo/libjpeg-turbo#157 (comment) ).
For fixing that, I suggest the following:
Add tjGetErrorCode
to the imports:
self.__get_error_code = turbo_jpeg.tjGetErrorCode
self.__get_error_code.argtypes = [
c_void_p
]
self.__get_error_code.restype = c_int
instead of immediately raising the IOError
exception if the status
is not 0, use that function to check if it's a warning:
import warnings
[...]
err_code = self.__get_error_code(handle)
if status != 0:
if err_code == 0:
warnings.warn(self.__get_error_str().decode())
else:
raise IOError(self.__get_error_str().decode(), err_code)
(unfortunately, I'm limited in what code I can commit as PR, so writing that stuff here, so you can do that)
Thanks!
hi, how about benchmark details? such as jpeg image information, cv2 build information? I wonder the speed difference between when cv2 built with libjpegturbo.so and PyTurboJPEG
Hi there,
first, thanks for quickly adding the decode_header
function! :)
Just a small note: The arguments pixel_format=TJPF_BGR, scaling_factor=None
seem to be unused for the decode_header
, so I guess they could be removed?
Best
Lukas
Hi and thanks for your work,
I work with a file format converter for whole scan images (wsi), which are basically a large number of smaller images (tiles, for example 1024x1024 px) that tiled together form a large (10s of Gigapixels) image. For the conversion, I have to merge together jpeg images to a larger image (e.g. 4096x1024) and split that image into a tiles of wanted size (e.g. 4 tiles of 1024x1024). The crop function in PyTurboJPEG is very useful for this last step, however for this specific application there are 2 'issues' that could make it better:
I have solved these issues in a way that fits my need by sub-classing the TurboJPEG-class. If these issues something that is interesting to more users and thus something that relevant for inclusion in your library, I would be happy to work with you.
Best regards
Erik Gabrielsson
Traceback (most recent call last):
File "D:/pythonproj/school/dipclass/dipex4.py", line 202, in
homework1()
File "D:/pythonproj/school/dipclass/dipex4.py", line 184, in homework1
jpeg = TurboJPEG('E:/libjpeg-turbo-gcc/')
Hi,
I noticed that when using libjpeg-turbo 3.0.0 markers are removed regardless of copynone
being set:
libjpeg-turbo 3.0.0:
turbo = TurboJPEG(r"C:\tools\libjpeg-turbo64-3.0.0\bin\turbojpeg.dll")
cropped = turbo.crop_multiple(tile_2, [(0, 0, 32, 32)], copynone=False)[0]
print(cropped[0:30])
>> b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x06\x04\x05\x06\x05'
libjpeg-turbo 2.1.*
turbo = TurboJPEG(r"C:\tools\libjpeg-turbo64\bin\turbojpeg.dll")
cropped = turbo.crop_multiple(tile_2, [(0, 0, 32, 32)], copynone=False)[0]
print(cropped[0:30])
>> b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xfe\x00)Hamama'
On 2.1.* I get the FF-FE comment marker followed by the comment, on 3.0.0 it is missing.
Setting copynone=True on 2.1.* produces the desired output (no comment marker). Also jpegtran in 3.0.0 seems to work with the different copy-switches.
when i use
img = cv2.imread("cong.jpg")
the picture is been correctly displayed.But when i use
in_file = open("cong.jpg", 'rb')
img = jpeg.decode(in_file.read())
in_file.close()
the picture loses its rotation info which leads to wrong display.
You can try and hope that you can fix this bug.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.