What's the size
import cv2
img = cv2.imread('./imgs/img.png', cv2.IMREAD_COLOR)
print(img.shape[0], img.shape[1]) # 182 277
print(img.size) # 151242
I thought img.size = img.width * img.width, but obviously wrong, check this example.
so, what's the definition of it?
It's all about what language you're working in.
The Python interface to OpenCV uses numpy, and numpy arrays have different methods from cv::Mat (C++). size means different things then.
Python: OpenCV uses numpy arrays. .size tells you the number of elements in the array. That is the product of all values in .shape.
C++: OpenCV uses its own cv::Mat. .size there is of type MatSize. It is roughly equivalent to .shape with numpy. Documentation indicates that it can also tell you the number of channels. It has an operator[] defined.
Related
I get that in Python OpenCV images are numpy arrays, that correspond to cv::Mat in c++.
This question is about what type-hint to put into python functions to properly restrict for OpenCV images (maybe even for a specific kind of OpenCV image).
What I do now is:
import numpy as np
import cv2
Mat = np.ndarray
def my_fun(image: Mat):
cv2.imshow('display', image)
cv2.waitKey()
Is there any better way to add typing information for OpenCV images in python?
You can specify it as numpy.typing.NDArray with an entry type. For example,
import numpy as np
Mat = np.typing.NDArray[np.uint8]
def my_fun(img: Mat):
pass
I've been searching for a way to use the vignette functionality of ImageMagick with Wand, but I can't find how to do it in Wand's documentation.
Is there a well hidden way to do it, and if there isn't, what are the alternatives ?
For python's wand library, you'll need to implement the C-API with library.api module.
import ctypes
from wand.api import library
from wand.image import Image
# Define C-API
library.MagickVignetteImage.argtypes = [ctypes.c_void_p, # Wand
ctypes.c_double, # Radius
ctypes.c_double, # Sigma
ctypes.c_long, # x
ctypes.c_long] # y
# Warning: `x' & `y' are ssize_t. Usually the same size as size_t, or long type.
# Extent Image class with new method
class VignetteImage(Image):
def vignette(self, radius=0.0, sigma=0.0, offset_x=0, offset_y=0):
library.MagickVignetteImage(self.wand,
radius,
sigma,
offset_x,
offset_y)
# Usage
with VignetteImage(filename="rose:") as rose:
rose.vignette(0.0, 5.0)
rose.save(filename="RoseVignette.png")
See http://www.imagemagick.org/api/magick-image.php#MagickVignetteImage in the ImageMagick Wand documentation
Sorry, I do not code in MagickWand or any other API. I assume you just could not find the documentation for vignette.
OOPS! I see you want a different Wand. Sorry, I cannot help with that. I do not see anything but drawing in your Wand documentation.
What language alternative are you looking for? You can do it directly in ImageMagick (likely PythonMagick).
See also opencv, python and numpy vignette at https://www.packtpub.com/mapt/book/application_development/9781785283932/2/ch02lvl1sec25/creating-a-vignette-filter
See also https://github.com/alexjohnson505/image-filter/blob/master/image-filter.py
See also https://imagepy.wordpress.com/2015/11/21/raw-image-processing-in-python-an-example/
I'm using pi3d to display an ImageSprite on the screen the texture of which comes form an image I'm loading.
displayTexture = pi3d.Texture("display/display.jpg", blend=True, mipmap=True)
displaySlide = pi3d.ImageSprite(texture=displayTexture, shader=shader, w=800, h=600)
This texture image is actually something I'm creating in-program. It's an openCV2 image and therefore just a numpy array. At the moment I'm saving it just to load it again as a texture, but is there a way to just constantly update the texture of the sprite with the changing numpy array values?
I looked into the openCV OpenGL support but from what I could see it only supports Windows at this stage and is therefore not suitable for this use.
Edit: Should have mentioned I'm happy for a lower level solution too. I'm currently trying to use .toString() on the image array and use the resulting byte list with glTexImage2D to produce a texture but no dice so far.
Yes you can pass a PIL.Image to pi3d.Texture and it will create a new Texture using that. There is a bit of work involved there so it will impact on frame rate if it's a big Texture. Also you need to update the pointer in the Buffer that holds the Texture array so the new Texture gets used.
There is a method to load a numpy array to a PIL.Image (Image.fromarray()) so this would be an easy route. However it's a bit convoluted as pi3d already converts the PIL.Image into a numpy array see https://github.com/tipam/pi3d/blob/master/pi3d/Texture.py#L163
The following works ok as a short-cut into the workings of pi3d.Texture but it's a bit of a hack calling the 'private' function _load_opengl. I might look at making a more robust method of doing this (i.e. for mapping videos to 3D objects etc)
#!/usr/bin/python
from __future__ import absolute_import, division, print_function, unicode_literals
import demo
import pi3d
import random
import numpy as np
from PIL import Image, ImageDraw
DISPLAY = pi3d.Display.create(x=150, y=150)
shader = pi3d.Shader("uv_flat")
im = Image.open("textures/PATRN.PNG")
#draw = ImageDraw.Draw(im) # there are various PIL libraries you could use
nparr = np.array(im)
tex = pi3d.Texture(im) # can pass PIL.Image rather than path as string
sprite = pi3d.ImageSprite(tex, shader, w=10.0, h=10.0)
mykeys = pi3d.Keyboard()
while DISPLAY.loop_running():
#draw.line((random.randint(0,im.size[0]),
# random.randint(0,im.size[1]),
# random.randint(0,im.size[0]),
# random.randint(0,im.size[1])), fill=128) # draw random lines
#nparr = np.array(im)
nparr += np.random.randint(-2, 2, nparr.shape) # random noise
tex.image = nparr
tex._load_opengl()
sprite.draw()
if mykeys.read() == 27:
mykeys.close()
DISPLAY.destroy()
break
PS I can't remember what version of pi3d the switch to numpy textures happened but it's quite recent so you probably have to upgrade
EDIT:
The switch from Texture.image being a bytes object to numpy array was v1.14 posted on 18Mar15
To clarify the steps to use a numpy array to initialise and refresh a changing image:
...
im = Image.fromarray(cv2im) # cv2im is a numpy array
tex = pi3d.Texture(im) # create Texture from PIL image
sprite = pi3d.ImageSprite(tex, shader, w=10.0, h=10.0)
...
tex.image = cv2im # set Texture.image to modified numpy array
tex._load_opengl() # re-run OpenGLESv2 routines
So, I'm generating a binary (well, really gray scale, 8bit, used as binary) image with python and opencv2, writing a small number of polygons to the image, and then dilating the image using a kernel. However, my source and destination image always end up the same, no matter what kernel I use. Any thoughts?
from matplotlib import pyplot
import numpy as np
import cv2
binary_image = np.zeros(image.shape,dtype='int8')
for rect in list_of_rectangles:
cv2.fillConvexPoly(binary_image, np.array(rect), 255)
kernel = np.ones((11,11),'int')
dilated = cv2.dilate(binary_image,kernel)
if np.array_equal(dilated, binary_image):
print("EPIC FAIL!!")
else:
print("eureka!!")
All I get is EPIC FAIL!
Thanks!
So, it turns out the problem was in the creation of both the kernel and the image. I believe that openCV expects 'uint8' as a data type for both the kernel and the image. In this particular case, I created the kernel with dtype='int', which defaults to 'int64'. Additionally, I created the image as 'int8', not 'uint8'. Somehow this did not trigger an exception, but caused the dilation to fail in a surprising fashion.
Changing the above two lines to
binary_image = np.zeros(image.shape,dtype='uint8')
kernel = np.ones((11,11),'uint8')
Fixed the problem, and now I get EUREKA! Hooray!
I have a pygame Surface and would like to invert the colors. Is there any way quicker & more pythonic than this? It's rather slow.
I'm aware that subtracting the value from 255 isn't the only definition of an "inverted color," but it's what I want for now.
I'm surprised that pygame doesn't have something like this built in!
Thanks for your help!
import pygame
def invertImg(img):
"""Inverts the colors of a pygame Screen"""
img.lock()
for x in range(img.get_width()):
for y in range(img.get_height()):
RGBA = img.get_at((x,y))
for i in range(3):
# Invert RGB, but not Alpha
RGBA[i] = 255 - RGBA[i]
img.set_at((x,y),RGBA)
img.unlock()
Taken from: http://archives.seul.org/pygame/users/Sep-2008/msg00142.html
def inverted(img):
inv = pygame.Surface(img.get_rect().size, pygame.SRCALPHA)
inv.fill((255,255,255,255))
inv.blit(img, (0,0), None, BLEND_RGB_SUB)
return inv
This may do the alpha channel wrong, but you should be able to get that working with additional tweaks.
Winston's answer is nice, but for the sake of completeness, when one has to manipulate an image pixel-by-pixel in Python, one should avoid looping through every pixel, no matter which image library is in use. This is CPU-intensive due to the nature of the language, and can rarely be made to work in realtime.
Fortunately, the excellent NumPy library can help perform several scalar operations in streams of bytes, looping over each number in native code, which is orders of magnitude faster than doing it solely in Python. For this particular operation, if we use an xor operation with (2^32 - 1), we can delegate the operation to the inner loop in native code.
This example, which you can paste directly into your Python console, will flip the pixels instantly to white (if you have NumPy installed):
import pygame
srf = pygame.display.set_mode((640,480))
pixels = pygame.surfarray.pixels2d(srf)
pixels ^= 2 ** 32 - 1
del pixels
pygame.display.flip()
Without NumPy installed, pygame.surfarray methods return ordinary Python arrays (from the stdlib array module) and you would have to find another way to operate on these numbers, since the ordinary Python array does not operate on all elements when a line such as pixels ^= 2 ** 32 - 1 is given.
One approach that might be more efficient would be to use PIL, as described here: How to invert colors of image with PIL (Python-Imaging)?
It's easy to convert it to a native pygame image in-memory, as described here: http://mail.python.org/pipermail/image-sig/2005-May/003315.html