Making a copy of an image - python

I am supposed to create several functions for my python program, and each program requires me to work with a copy of an input image. Hence, I need to write img = image.copy() for every function in my code. However, when I run the code, I am returned an AttributeError saying "'tuple' object has no attribute 'copy'. "
Given that I still have to include the statement img = image.copy() somewhere inside my function, how do I go about changing my code to remove this error? Do I need to change the image into numpy array first before I can use copy()?
Code:
def func(image):
img = image.copy() #error code appeared here
np_img = np.array(image)
rsize, csize = len(img), len(img[0]) #denoting the rows and columns of pixels of the image respectively
(the rest of the code)
Error message: AttributeError: 'tuple' object has no attribute 'copy'

Given that you have to put img=image.copy() in your functions, the easiest way should be to flip the order of np_img = np.array(image) and the former line. I'm assuming that your argument image has not been converted into a numpy array prior to what we see here.
After that, you should change img=image.copy() to the appropriate variables.
That said, I think it's best to load the image as a numpy matrix right away before doing anything else. That way, you can make a copy before any of your functions, lowering each function's costs too.

Related

OpenCV TypeError: Expected cv::UMat for argument 'src' - What is this?

Disclaimer: huge openCV noob
Traceback (most recent call last):
File "lanes2.py", line 22, in
canny = canny(lane_image)
File "lanes2.py", line 5, in canny
gray = cv2.cvtColor(imgUMat, cv2.COLOR_RGB2GRAY)
TypeError: Expected cv::UMat for argument 'src'
What exactly is 'src' referring to?
src is the first argument to cv2.cvtColor.
The error you are getting is because it is not the right form. cv2.Umat() is functionally equivalent to np.float32(), so your last line of code should read:
gray = cv2.cvtColor(np.float32(imgUMat), cv2.COLOR_RGB2GRAY)
gray = cv2.cvtColor(cv2.UMat(imgUMat), cv2.COLOR_RGB2GRAY)
UMat is a part of the Transparent API (TAPI) than help to write one code for the CPU and OpenCL implementations.
The following can be used from numpy:
import numpy as np
image = np.array(image)
Not your code is the problem this is perfectly fine:
gray = cv2.cvtColor(imgUMat, cv2.COLOR_RGB2GRAY)
The problem is that imgUMat is None so you probably made a mistake when loading your image:
imgUMat = cv2.imread("your_image.jpg")
I suspect you just entered the wrong image path.
Just add this at start:
image = cv2.imread(image)
Convert your image matrix to ascontiguousarray using np.ascontiguousarray as bellow:
gray = cv2.cvtColor(np.ascontiguousarray(imgUMat), cv2.COLOR_RGB2GRAY)
Is canny your own function? Do you use Canny from OpenCV inside it? If yes check if you feed suitable argument for Canny - first Canny argument should meet following criteria:
type: <type 'numpy.ndarray'>
dtype: dtype('uint8')
being single channel or simplyfing: grayscale, that is 2D array, i.e. its shape should be 2-tuple of ints (tuple containing exactly 2 integers)
You can check it by printing respectively
type(variable_name)
variable_name.dtype
variable_name.shape
Replace variable_name with name of variable you feed as first argument to Canny.
This is a general error, which throws sometimes, when you have mismatch between the types of the data you use. E.g I tried to resize the image with opencv, it gave the same error. Here is a discussion about it.
Some dtype are not supported by specific OpenCV functions. For example inputs of dtype np.uint32 create this error. Try to convert the input to a supported dtype (e.g. np.int32 or np.float32)
that is referring to the expected dtype of your image
"image".astype('float32') should solve your issue
Sometimes I have this error when videostream from imutils package doesn't recognize frame or give an empty frame. In that case, solution will be figuring out why you have such a bad frame or use a standard VideoCapture(0) method from opencv2
If using ImageGrab
Verify that your image is not a 0x0 area due to an incorrect bbox.
Verify the application root folder is the same as the file you are attempting to run.
I got round thid by writing/reading to a file. I guessed cv.imread would put it into the format it needed. This code for anki Vector SDK program but you get the idea.
tmpImage = robot.camera.latest_image.raw_image.save('temp.png')
pilImage = cv.imread('temp.png')
If you are using byte object instead of reading from file you can convert your image to numpy array like this
image = numpy.array(Image.open(io.BytesIO(image_bytes)))

upsizing images with cubic interpolation in python PIL

Hi I am simply trying to resize a batch of images of size (a,b,3) to a bigger size (c, d, 3) (c>a, d>b) using cubic interpolation. But whenever I opened the resized images again after I seemingly resized successfully in the first place, I found the old dimension... It happened to every image and every dimension in my trials... Could anyone kindly point out what I was missing? Thanks a lot!
Here is my code:
from PIL import Image
im = Image.open("img0.jpg").convert("RGB")
im # the original size
<PIL.Image.Image image mode=RGB size=600x337 at 0x102D83450>
im.resize((800,400),Image.BICUBIC)
<PIL.Image.Image image mode=RGB size=800x400 at 0x102D834D0> # thought I was doing it right
im.save("resized.jpg")
im=Image.open("resized.jpg").convert("RGB")
im
<PIL.Image.Image image mode=RGB size=600x337 at 0x102D83490> # and the actual size seems even smaller than before!
The image resizing does not happen in-place. A new, resized image is returned, so you must save it.
new_img = im.resize((800,400),Image.BICUBIC)
new_img.save("resized.jpg")
or
im.resize((800,400),Image.BICUBIC).save("resized.jpg")
Whether or not a method or a function makes changes "in place" (which means there's no return value to grab and use, and a value of None is returned) or returns a value which you must use depends on the creator of the method or function. You can always, through trial and error, figure this out, but the better way is to look at the docs. For example, for the resize() method of PIL/Pillow, look at https://pillow.readthedocs.io/en/3.4.x/reference/Image.html#PIL.Image.Image.resize
There you will see that the function
Returns a resized copy of this image.
That tells you that you have to do something with the return value in order to preserve the effects of the method.
Additionally, if you go to http://effbot.org/imagingbook/image.htm and jump down to resize, you 'll see it says:
resize #
im.resize(size) ⇒ image
im.resize(size, filter) ⇒ image
The "arrow" pointing to the right is notation which says that the method returns a value. In this case, it returns an image.

OpenCV python canny Required argument 'threshold2' (pos 4) not found

I am trying to isolate text from an image with openCV before sending it to tesseract4 engine to maximize results.
I found this interesting post and I decided to copy the source and try by mysdelf
However I am getting issue with the first call to OpenCV
To reproduce:
Simply copy the code from the gist
launch command script.py /path/to/image.jpg
I am getting issue:
Required argument 'threshold2' (pos 4) not found
Do you maybe have an idea of what does it means.
I am a javascript, java and bash script developer but not python...
In a simple version:
import glob
import os
import random
import sys
import random
import math
import json
from collections import defaultdict
import cv2
from PIL import Image, ImageDraw
import numpy as np
from scipy.ndimage.filters import rank_filter
if __name__ == '__main__':
if len(sys.argv) == 2 and '*' in sys.argv[1]:
files = glob.glob(sys.argv[1])
random.shuffle(files)
else:
files = sys.argv[1:]
for path in files:
out_path = path.replace('.jpg', '.crop.png')
if os.path.exists(out_path): continue
orig_im = Image.open(path)
edges = cv2.Canny(np.asarray(orig_im), 100, 200)
Thanks in advance for your help
Edit: okay so this answer is apparently wrong, as I tried to send my own 16-bit int image into the function and couldn't reproduce the results.
Edit2: So I can reproduce the error with the following:
from PIL import Image
import numpy as np
import cv2
orig_im = Image.open('opencv-logo2.png')
threshold1 = 50
threshold2 = 150
edges = cv2.Canny(orig_im, 50, 100)
TypeError: Required argument 'threshold2' (pos 4) not found
So if the image was not cast to an array, i.e., the Image class was passed in, I get the error. The PIL Image class is a class with a lot of things other than the image data associated to it, so casting to a np.array is necessary to pass into functions. But if it was properly cast, everything runs swell for me.
In a chat with Dan Mašek, my idea below is a bit incorrect. It is true that the newer Canny() method needs 16-bit images, but the bindings don't look into the actual numpy dtype to see what bit-depth it is to decide which function call to use. Plus, if you try to actually send a uint16 image in, you get a different error:
edges = cv2.Canny(np.array([[0, 1234], [1234, 2345]], dtype=np.uint16), 50, 100)
error: (-215) depth == CV_8U in function Canny
So the answer I originally gave (below) is not the total culprit. Perhaps you accidentally removed the np.array() casting of the orig_im and got that error, or, something else weird is going on.
Original (wrong) answer
In OpenCV 3.2.0, a new method for Canny() was introduced to allow users to specify their own gradient image. In the original implementation, Canny() would use the Sobel() operator for calculating the gradients, but now you could calculate say the Scharr() derivatives and pass those into Canny() instead. So that's pretty cool. But what does this have to do with your problem?
The Canny() method is overloaded. And it decides which function you want to use based on the arguments you send in. The original call for Canny() with the required arguments looks like
cv2.Canny(image, threshold1, threshold2)
but the new overloaded method looks like
cv2.Canny(grad_x, grad_y, threshold1, threshold2)
Now, there was a hint in your error message:
Required argument 'threshold2' (pos 4) not found
Which one of these calls had threshold2 in position 4? The newer method call! So why was that being called if you only passed three args? Note that you were getting the error if you used a PIL image, but not if you used a numpy image. So what else made it assume you were using the new call?
If you check the OpenCV 3.3.0 Canny() docs, you'll see that the original Canny() call requires an 8-bit input image for the first positional argument, whereas the new Canny() call requires a 16-bit x derivative of input image (CV_16SC1 or CV_16SC3) for the first positional argument.
Putting two and two together, PIL was giving you a 16-bit input image, so OpenCV thought you were trying to call the new method.
So the solution here, if you wanted to continue using PIL, is to convert your image to an 8-bit representation. Canny() needs a single-channel (i.e. grayscale) image to run, first off. So you'll need to make sure the image is single-channel first, and then scale it and change the numpy dtype. I believe PIL will read a grayscale image as single channel (OpenCV by default reads all images as three-channel unless you tell it otherwise).
If the image is 16-bit, then the conversion is easy with numpy:
img = (img/256).astype('uint8')
This assumes img is a numpy array, so you would need to cast the PIL image to ndarray first with np.array() or np.asarray().
And then you should be able to run Canny() with the original function call.
The issue was coming from an incompatibility between interfaces used and openCV version.
I was using openCV 3.3 so the correct way to call it is:
orig_im = cv2.imread(path)
edges = cv2.Canny(orig_im, 100, 200)

opencv zoom function strange results

i am trying to write a zoom function which looks something like this:
centre = ((im.width-1)/2, (im.height-1)/2)
width = int(im.width/(2.0*level))
height = int(im.height/(2.0*level))
rect = (centre[0]-width, centre[1]-height, width*2, height*2)
dst = cv.GetSubRect(im, rect)
cv.Resize(dst, im)
when I use exactly what is written above, I get an odd result where the bottom half of the resultant image is distorted and blurry. However when I replace the line cv.Resize(dst, im) with
size = cv.CloneImage(im)
cv.Resize(dst, size)
im = size
it works fine. Why is this? is there something fundamentally wrong with the way i am performing the zoom?
cv.Resize requires source and destination to be separate memory locations.
Now in the first snippet of your code, you are using cv.GetSubRect to generate an object pointing to area of image which you wish to zoom in. Here the new object is NOT pointing to a new memory location. It is pointing to a memory location which is a subset of original object.
Since cv.Resize requires both the memory locations to be different, what you are getting is a result of undefined behavior.
In the second part of your code you are fulfilling this criteria by using cv.CloneImage.
you are first creating a copy of im (i.e. size. however you could have used a blank image aswell) and then you are using cv.Resize to resize dst and write the resulting image in size.
My advice is to go through the function documentation before using them.

Python/OpenCV: Converting images taken from capture

I'm trying to convert images taken from a capture (webcam) and do some processing on them with OpenCV, but I'm having a difficult time..
When trying to convert the image to grayscale, the program crashes. (Python.exe has stopped working)
Here is the main snippet of my code:
newFrameImageGS = cv.CreateImage ((320, 240), cv.IPL_DEPTH_8U, 1)
for i in range(0,5):
newFrameImage = cv.QueryFrame(ps3eye)
cv.CvtColor(newFrameImage,newFrameImageGS,cv.CV_BGR2GRAY)
golfSwing.append(newFrameImageGS)
When I try using cvConvertScale I get the assertion error:
src.size() == dst.size() && src.channels() == dst.channels()
which makes sense, but I'm pretty confused on how to go about converting the input images of my web cam into images that can be used by functions like cvUpdateMotionHistory() and cvCalcOpticalFlowLK()
Any ideas? Thanks.
UPDATE:
I converted the image to grayscale manually with this:
for row in range(0,newFrameImage.height):
for col in range(0,newFrameImage.width):
newFrameImageGS[row,col] = (newFrameImage8U[row,col][0] * 0.114 + # B
newFrameImage8U[row,col][1] * 0.587 + # G
newFrameImage8U[row,col][2] * 0.299) # R
But this takes quite a while.. and i still can't figure out why cvCvtColor is causing the program to crash.
For some reason, CvtColor caused the program to crash when the image depths where 8 bit. When I converted them to 32 bit, the program no longer crashed and everything seemed to work OK. I have no idea why this is, but at least it works now.
newFrameImage = cv.QueryFrame(ps3eye)
newFrameImage32F = cv.CreateImage((320, 240), cv.IPL_DEPTH_32F, 3)
cv.ConvertScale(newFrameImage,newFrameImage32F)
newFrameImageGS_32F = cv.CreateImage ((320,240), cv.IPL_DEPTH_32F, 1)
cv.CvtColor(newFrameImage32F,newFrameImageGS_32F,cv.CV_RGB2GRAY)
newFrameImageGS = cv.CreateImage ((320,240), cv.IPL_DEPTH_8U, 1)
cv.ConvertScale(newFrameImageGS_32F,newFrameImageGS)
There is a common mistake here:
You're creating a single image in the newFrameImageGS variable before the loop, then overwrite its contents in the loop, which is then appended to a list. The result will not be what you would expect. The list will contain five references to the same image instance at the end, since only the object reference is appended to the list, no copy of the object made this way. This image will contain the very last frame, so you get five of that frame as a result, which is not what you want, I guess. Please review the Python tutorial if it is not clear for you. You can solve this by moving the first line of the above code into the body of the for loop.
Another possibilities if fixing the above would not help you:
The CvtColor function seems to be the correct one for conversion to grayscale, since it can convert to a different number of channels.
According to this manual the CvtColor function requires a destination image of the same data type as the source. Please double check that newFrameImage is a IPL_DEPTH_8U image.

Categories