Reading a PyCBitmap with OpenCV - python

I created an image from a window screenshot using Win32gui. The object has the type:
object 'PyCBitmap' - assoc is 000002AF9A64DB50, vi=<None>
I want to then pass this for analysis with OpenCV. I have had success reading in a saved .bmp file using:
cv2.imread(img_file, 0)
When trying using cv2.imread for a PyCBitmap object I get the following error:
TypeError: bad argument type for built-in operation
My question is:
How can I convert the PyCBitmap object into an acceptable type for cv2.imread, without having to save the object as a .bmp file first?
Thanks in advance,
Behzad
p.s I'm using opencv 3.1 with python bindings, I'm happy to follow advice written in C++ or python :)

I've been looking for the same thing, and I finally found it by combining several other SO answers:
PIL and Bitmap from WinAPI
https://stackoverflow.com/a/14140796/343381
Basically, the code I came up with is:
import PIL, numpy, cv2
bmpinfo = dataBitMap.GetInfo()
bmparray = numpy.asarray(dataBitMap.GetBitmapBits(), dtype=numpy.uint8)
pil_im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmparray, 'raw', 'BGRX', 0, 1)
pil_array = numpy.array(pil_im)
cv_im = cv2.cvtColor(pil_array, cv2.COLOR_RGB2BGR)
Brief explanation: Python OpenCV just uses numpy arrays, so the trick is really getting the bytes into the right numpy array format. As it turns out, for this you need an image processing library like PIL that can handle the image-specific logic like cutting out the alpha channel. The input data is generally RGBX format, PIL converts to RGB, and OpenCV converts that to BGR which it likes.
I profiled this and unfortunately it is dramatically slower than GetBitmapBits() and in converting its tuple result to an array.

Related

Why does cv2.imread output a matrix of zeros for a 32-bit image even when using cv.IMREAD_ANYDEPTH?

I'm using OpenCV version 4.1.1 in Python and cannot get a legitimate reading for a 32-bit image, even when I use cv.IMREAD_ANYDEPTH. Without cv.IMREAD_ANYDEPTH, it returns as None type; with it, I get a matrix of zeros. The issue persists after reinstalling OpenCV. os.path.isfile returns True. The error was replicated on another computer. The images open in ImageJ, so I wouldn't think they're corrupted. I would rather use Skimage since it reads the images just fine, but I have to use OpenCV for what I'm working on. Any advice is appreciated.
img = cv2.imread(file,cv2.IMREAD_ANYDEPTH)
Link for the image: https://drive.google.com/file/d/1IiHbemsmn2gLW12RG3i9fLYZQW2u8sQw/view?usp=sharing
It appears to be some bug in how OpenCV loads such TIFF images. Pillow seems to load the image in a sensible way. Running
from PIL import Image
import numpy as np
img_pil = Image.open('example_image.tiff')
img_pil_cv = np.array(img_pil)
print(img_pil_cv.dtype)
print(img_pil_cv.max())
I get
int32
40950
as an output, which looks reasonable enough.
When I do
import cv2
img_cv = cv2.imread('example_image.tiff', cv2.IMREAD_ANYDEPTH)
print(img_cv.dtype)
print(img_cv.max())
I get
float32
5.73832e-41
which is obviously wrong.
Nevertheless, the byte array holding the pixel data is correct, it's just not being interpreted correctly. You can use numpy.ndarray.view to reinterpret the datatype of a numpy array, so that it's treated as an array if 32bit integers instead.
img_cv = cv2.imread('example_image.tiff', cv2.IMREAD_ANYDEPTH)
img_cv = img_cv.view(np.int32)
print(img_cv.dtype)
print(img_cv.max())
Which prints out
int32
40950
Since the maximum value is small enough for 16bit integer, let's convert the array and see what it looks like
img_cv_16bit = img_cv.astype(np.uint16)
cv2.imwrite('output_cv_16bit.png', img_cv_16bit)
OK, there are some bright spots, and a barely visible pattern. With a little adjustment, we can get something visible:
img_cv_8bit = np.clip(img_cv_16bit // 16, 0, 255).astype(np.uint8)
cv2.imwrite('output_cv_8bit.png', img_cv_8bit)
That looks quite reasonable now.

Error when overlaying two images in OpenCV and or PIL

I've tried overlaying two images in openCV both in openCV and in PIL, but to no avail. I'm using a 1000x1000x3 array of np.zeros for the background (aka, a black background) and this random image of my monitor, but I really can't get it to work for some reason unbeknownst to me.
Trying with OpenCV only: (result(if you pay attention, you can see a couple of weird lines and dots in the middle))
base_temp = np.zeros((1000,1000,3))
foreground_temp = cv2.imread('exampleImageThatILinkedAbove.png')
base_temp[offset_y:offset_y+foreground_temp.shape[0], offset_x:offset_x+foreground_temp.shape[1]] = foreground_temp
Trying with PIL: (The result is literally the same as the OpenCV version)
base_temp = cv2.convertScaleAbs(self.base) #Convert to uint8 for cvtColor
base_temp = cv2.cvtColor(base_temp, cv2.COLOR_BGR2RGB) #PIL uses RGB and OpenCV uses BGR
base_temp = Image.fromarray(base_temp) #Convert to PIL Image
foreground_temp = cv2.cvtColor(cv2.convertScaleAbs(self.last_img), cv2.COLOR_BGR2RGB)
foreground_temp = Image.fromarray(foreground_temp)
base_temp.paste(foreground_temp, offset)
I'm using python3.5 and and OpenCV3.4 on Windows 10, if that's any help.
I'd like to avoid any solutions that require saving the cv2 images and then reloading them in another module to convert them but if it's unavoidable that's okay too. Any help would be appreciated!
If you check the type of base_temp, you will see it is float64 and that is going to cause you problems when you try to save it as a JPEG which expects unsigned 8-bit values.
So the solution is to create your base_temp image with the correct type:
base_temp = np.zeros((1000,1000,3), dtype=np.uint8)
The complete code and result look like this:
import cv2
import numpy as np
from PIL import Image
# Make black background - not square, so it shows up problems with swapped dimensions
base_temp=np.zeros((768,1024,3),dtype=np.uint8)
foreground_temp=cv2.imread('monitor.png')
# Paste with different x and y offsets so it is clear when indices are swapped
offset_y=80
offset_x=40
base_temp[offset_y:offset_y+foreground_temp.shape[0], offset_x:offset_x+foreground_temp.shape[1]] = foreground_temp
Image.fromarray(base_temp).save('result.png')

OpenCV Python converting color-space image to YCbCr

I need convert an image from BGR to YCbCr in Python using OpenCV.
I have an image with size/resolution 512x512, but when the image is opened, the size is 128x128.
I'm doing:
image = cv2.imread(imageName, cv2.COLOR_BGR2YCR_CB)
Could anyone help me?
The problem:
If you look at the docs for imread, the function takes an integer flag called imreadmodes. This flag seems to accept information about resizing the image, rather than changing color spaces.
The solution:
I believe you are looking for the cv2.cvtColor function which uses a flag to determine the source and destination colorspaces.
Both flags are simple integer enumerations. I assume the imread function is simply doing the best it can with the wrong type of flag.
You probably want to do something like:
BGRImage = cv2.imread(imageName)
YCrCbImage = cv2.cvtColor(BGRImage, cv2.COLOR_BGR2YCR_CB)

Load OpenCV image from binary string

What I mean by binary string, is the raw content of image file (That's what wand.image.make_blob() returns)
Is there a way to load it in OpenCV ?
Edit:
cv2.imdecode() doesn't work
img = cv2.imdecode( buf=wand_img.make_blob(), flags=cv2.IMREAD_UNCHANGED)
TypeError: buf is not a numpy array, neither a scalar
Have you tried cv2.imdecode which takes an image buffer and turns it into a CvMat object? Though I am not sure about this one.
See : http://docs.opencv.org/3.0-beta/modules/imgcodecs/doc/reading_and_writing_images.html

What is the best way to load a CCITT T.3 compressed tiff using python?

I am trying to load a CCITT T.3 compressed tiff into python, and get the pixel matrix from it. It should just be a logical matrix.
I have tried using pylibtiff and PIL, but when I load it with them, the matrix it returns is empty. I have read in a lot of places that these two tools support loading CCITT but not accessing the pixels.
I am open to converting the image, as long as I can get the logical matrix from it and do it in python code. The crazy thing is is that if I open one of my images in paint, save it without altering it, then try to load it with pylibtiff, it works. Paint re-compresses it to the LZW compression.
So I guess my real question is: Is there a way to either natively load CCITT images to matricies or convert the images to LZW using python??
Thanks,
tylerthemiler
It seems the best way is to not use Python entirely but lean on netpbm:
import Image
import ImageFile
import subprocess
tiff = 'test.tiff'
im = Image.open(tiff)
print 'size', im.size
try:
print 'extrema', im.getextrema()
except IOError as e:
print 'help!', e, '\n'
print 'I Get by with a Little Help from my Friends'
pbm_proc = subprocess.Popen(['tifftopnm', tiff],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(pbm_data, pbm_error) = pbm_proc.communicate()
ifp = ImageFile.Parser()
ifp.feed(pbm_data)
im = ifp.close()
print 'conversion message', pbm_error,
print 'extrema', im.getextrema()
print 'size', im.size
# houston: we have an image
im.show()
Seems to do the trick:
$ python g3fax.py
size (1728, 2156)
extrema help! decoder group3 not available
I Get by with a Little Help from my Friends
conversion message tifftopnm: writing PBM file
extrema (0, 255)
size (1728, 2156)
How about running tiffcp with subprocess to convert to LZW (-c lzw switch), then process normally with pylibtiff? There are Windows builds of tiffcp lying around on the web. Not exactly Python-native solution, but still...

Categories