I'm using Face Align and getting the following error for every image:
AttributeError: 'str' object has no attribute 'shape'
I interpret this to mean that my code is expecting an image object and instead receiving a string, correct?
The offending code:
def getAligns(self,
img,
use_cnn = False,
savepath = None,
return_info = False):
"""
get face alignment picture
:param img: original BGR image or a path to it
:param use_cnn: using CNN to extract aligned faces, if set, dlib
be compiled with cuda support
:param savepath: savepath, format "xx/xx/xx.png"
:param return_info: if set, return face positinos [(x, y, w, h)]
:return: aligned faces, (opt) rects
"""
print(img.shape)
if type(img) == str:
try:
img = cv2.imread(img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
except:
shutil.copy2(img, 'temp.jpg')
img = cv2.imread('temp.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
os.remove('temp.jpg')
Relevant code from Align_Faces:
if clean: clear_dir(config.aligned)
os.chdir(config.labeled)
jobs = glob.glob("*.jpg")
print(len(jobs))
## # un-parallel
for picname in jobs:
print(picname)
aligned = FL.getAligns(picname)
if len(aligned) != 1:
print(config.aligned)
print(picname)
print(aligned[0])
return cv2.imwrite(config.aligned + picname, aligned[0])
Full output:
Look at your data flow:
jobs = glob.glob("*.jpg")
## # un-parallel
for picname in jobs:
print(picname)
aligned = FL.getAligns(picname)
def getAligns(self,
img,
use_cnn = False,
savepath = None,
return_info = False):
print(img.shape)
Your posted output shows that you've maintained the file name as a string 0_0_ErinMurphy.jpg, and passed that string into getAligns. A string has no shape attribute. You've missed a conversion step, such as reading in the image.
You are passing the string with the image name to the function and then asking what is the shape of the string.
print(picname)
returns 0_0_ErinMurphy.jpg which is a string.
You need to import the image and then convert it to the pixels so that you can read its shape.
Related
I am trying to use webcam to collect a photo and resizing it using cv2.resize(). But I am getting this error. I am using google colab. tensorflow==2.4.1 tensorflow-gpu==2.4.1 opencv-python matplotlib. cv2.resize suppose to take numpy array and dimension details as parameter. But everytime I am using it giving me this error. When I comment it out code works fine.
def take_photo(filename='photo.jpg', quality=0.8):
js = Javascript('''
async function takePhoto(quality) {
const div = document.createElement('div');
const capture = document.createElement('button');
capture.textContent = 'Capture';
div.appendChild(capture);
const video = document.createElement('video');
video.style.display = 'block';
const stream = await navigator.mediaDevices.getUserMedia({video: true});
document.body.appendChild(div);
div.appendChild(video);
video.srcObject = stream;
await video.play();
// Resize the output to fit the video element.
google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
// Wait for Capture to be clicked.
await new Promise((resolve) => capture.onclick = resolve);
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
stream.getVideoTracks()[0].stop();
div.remove();
return canvas.toDataURL('image/jpeg', quality);
}
''')
display(js)
# get photo data
data = eval_js('takePhoto({})'.format(quality))
# get OpenCV format image
img = js_to_image(data)
# grayscale img
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
print(type(gray))
#print(gray.shape)
# get face bounding box coordinates using Haar Cascade
faces = face_cascade.detectMultiScale(gray)
# draw face bounding box on image
for (x,y,w,h) in faces:
img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
# save image
cv2.imwrite(filename, img)
return filename
## js_to_data function
# function to convert the JavaScript object into an OpenCV image
def js_to_image(js_reply):
"""
Params:
js_reply: JavaScript object containing image from webcam
Returns:
img: OpenCV BGR image
"""
# decode base64 image
image_bytes = b64decode(js_reply.split(',')[1])
# convert bytes to numpy array
jpg_as_np = np.frombuffer(image_bytes, dtype=np.uint8)
#Resize image
jpg_as_np= cv2.resize(jpg_as_np,(250,250))
jpg_as_np.shape
# decode numpy array into OpenCV BGR image
img = cv2.imdecode(jpg_as_np, flags=1) **#error here**
return img
# function to convert OpenCV Rectangle bounding box image into base64 byte string to be overlayed on video stream
def bbox_to_bytes(bbox_array):
"""
Params:
bbox_array: Numpy array (pixels) containing rectangle to overlay on video stream.
Returns:
bytes: Base64 image byte string
"""
# convert array into PIL image
bbox_PIL = PIL.Image.fromarray(bbox_array, 'RGBA')
iobuf = io.BytesIO()
# format bbox into png for return
bbox_PIL.save(iobuf, format='png')
# format return string
bbox_bytes = 'data:image/png;base64,{}'.format((str(b64encode(iobuf.getvalue()), 'utf-8')))
return bbox_bytes
from numpy.lib import type_check
try:
filename = take_photo()
print(type(filename))
print('Saved to {}'.format(filename))
Error: OpenCV(4.6.0) /io/opencv/modules/imgcodecs/src/loadsave.cpp:818: error: (-215:Assertion failed) buf.checkVector(1, CV_8U) > 0 in function 'imdecode_'
I'm working with MRI images and I'd like to use from_tensor_slices to preprocess the paths but I don't know how to use that properly. Below are my code, the problem message and link for the dataset.
First I rearrange my data. 484 images and 484 labels
image_data_path = './drive/MyDrive/Brain Tumour/Task01_BrainTumour/imagesTr/'
label_data_path = './drive/MyDrive/Brain Tumour/Task01_BrainTumour/labelsTr/'
image_paths = [image_data_path + name
for name in os.listdir(image_data_path)
if not name.startswith(".")]
label_paths = [label_data_path + name
for name in os.listdir(label_data_path)
if not name.startswith(".")]
image_paths = sorted(image_paths)
label_paths = sorted(label_paths)
Then, the function to load 1 example (I use nibabel to load nii files)
def load_one_sample(image_path, label_path):
image = nib.load(image_path).get_fdata()
image = tf.convert_to_tensor(image, dtype = 'float32')
label = nib.load(label_path).get_fdata()
label = tf.convert_to_tensor(label, dtype = 'uint8')
return image, label
Next, I tried using from_tensor_slices
image_filenames = tf.constant(image_paths)
label_filenames = tf.constant(label_paths)
dataset = tf.data.Dataset.from_tensor_slices((image_filenames, label_filenames))
all_data = dataset.map(load_one_sample)
And the error comes: TypeError: stat: path should be string, bytes, os.PathLike or integer, not Tensor
What can be wrong and how can I fix it?
Datalink: https://drive.google.com/drive/folders/1HqEgzS8BV2c7xYNrZdEAnrHk7osJJ--2 (task 1 - Brain Tumour)
Please tell me if you need more information.
nib.load is not a TensorFlow function.
If you want to use anything in tf.data pipeline that is not a TensorFlow function then you have to wrap it using a tf.py_function.
Code:
image_data_path = 'Task01_BrainTumour/imagesTr/'
label_data_path = 'Task01_BrainTumour/labelsTr/'
image_paths = [image_data_path + name
for name in os.listdir(image_data_path)
if not name.startswith(".")]
label_paths = [label_data_path + name
for name in os.listdir(label_data_path)
if not name.startswith(".")]
image_paths = sorted(image_paths)
label_paths = sorted(label_paths)
def load_one_sample(image_path, label_path):
image = nib.load(image_path.numpy().decode()).get_fdata()
image = tf.convert_to_tensor(image, dtype = 'float32')
label = nib.load(label_path.numpy().decode()).get_fdata()
label = tf.convert_to_tensor(label, dtype = 'uint8')
return image, label
def wrapper_load(img_path, label_path):
img, label = tf.py_function(func = load_one_sample, inp = [img_path, label_path], Tout = [tf.float32, tf.uint8])
return img, label
dataset = tf.data.Dataset.from_tensor_slices((image_paths, label_paths)).map(wrapper_load)
The error is not due to the from_tensor_slices function but arises as nibs.load is expecting a string but gets a tensor.
However, a better way would be to create tfrecords and use them to train the model.
I am working on a style transfer task, my model returns a tensor. recently I was saving that image using torchvision.utils
torchvision.utils.save_image(genarated_image, result_path)
now I have passed the same image to streamlit.
def image_input():
content_file = st.sidebar.file_uploader("Choose a Content Image", type=["png", "jpg", "jpeg"])
if content_file is not None:
content = Image.open(content_file)
content = np.array(content) # pil to cv
content = cv2.cvtColor(content, cv2.COLOR_RGB2BGR)
else:
st.warning("Upload an Image OR Untick the Upload Button)")
st.stop()
WIDTH = st.sidebar.select_slider('QUALITY (May reduce the speed)', list(range(150, 501, 50)), value=200)
content = imutils.resize(content, width=WIDTH)
generated = genarate_image(content)
st.sidebar.image(content, width=300, channels='BGR')
st.image(generated, channels='BGR', clamp=True)
But now streamlit giving me this error.
TypeError: a bytes-like object is required, not 'Tensor'
is there a way to convert tensor into a "bytes-like object" ?
It can be solved by transforming tonsor to PIL Image.
from torchvision import transforms
def trans_tensor_to_pil(tensor_img):
pil_image = transforms.ToPILImage()(tensor_img.squeeze_(0))
return pil_image
I'm storing two images in loaded_needle_images = [] and I'm checking they're loading correctly with cv.imshow('needle_img', self.needle_img)
def __init__(self, source_needle_images, method=cv.TM_CCOEFF_NORMED):
loaded_needle_images= []
for img in source_needle_images:
self.needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
loaded_needle_images.append(self.needle_img)
cv.imshow('needle_img', self.needle_img)
cv.waitKey(0)
When then when I pass them into a different function are they loading as 1 x 4 pixels shown below?
def find(self, haystack_img, loaded_needle_images, threshold=0.5, debug_mode=None):
processed_images = np.asarray(loaded_needle_images)
cv.imshow('processed image', processed_images)
cv.waitKey(0)
cv.imshow('loaded image', loaded_needle_images)
cv.waitKey(0)
Both imshow commands bring up the same image meaning it's been converted by asarray but I'm not sure how/why I'm going from an actual image to the four pixels shown below.
Update: To show where I'm calling find
# the window to capture
wincap = WindowCapture('x')
# load needle images and start the matching process
source_needle_images = glob.glob(r'C:\\\\\\\*.jpg')
search = Search(source_needle_images)
loop_time = time()
while(True):
# get an updated image of the game
haystack_img = wincap.get_screenshot()
# display the processed image
points = search.find(haystack_img, 0.85, 'rectangles')
Classes in Python don't have anyway to "automatically" pass values between functions. If you are creating data in an __init__ function, the only way you can get access to it again is to save it in the instance (ie, self). You can not return values from an __init__ function.
Updated __init__:
def __init__(self, source_needle_images, method=cv.TM_CCOEFF_NORMED):
self.loaded_needle_images= [] # create list in the instance
for img in source_needle_images:
self.needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
self.loaded_needle_images.append(self.needle_img) # update the list created in the instance
cv.imshow('needle_img', self.needle_img)
cv.waitKey(0)
Updated def find(...):
def find(self, haystack_img, threshold=0.5, debug_mode=None): # don't need to pass in "loaded_needle_images"
if len(self.loaded_needle_images) < 1:
raise AttributeError("Object not properly initialized, no loaded_needle_images") # adding for extra error processing to catch if loaded_needle_images is empty before attempting to run.
processed_images = np.asarray(self.loaded_needle_images) # grab loaded_needle_images from instance
cv.imshow('processed image', processed_images)
cv.waitKey(0)
cv.imshow('loaded image', self.loaded_needle_images) # grab loaded_needle_images from instance
cv.waitKey(0)
I'm using OpenCV to detect images in real-time. This is working with single images. I'm trying to update the script to work with multiple images at once with the end goal to be to detect a few different elements on the screen at once and draw rectangles around them while returning the centre points of the images for use elsewhere.
I've gotten as far as passing the images in from my main.py file and initiating the search.
# load source images
source_images = glob.glob(r'C:\\\\\\\*.jpg')
# perform the search
search = Search(source_images)
I can't seem to pass loaded_images into my find function. I've tried defining loaded_images in the Class properties and within the constructor but neither work and give me the error NameError: name 'loaded_images' is not defined
class Search:
# properties
needle_img = None
needle_w = 0
needle_h = 0
method = None
# empty list to store the source images
#loaded_images = [] <-------------------------------------------------
# constructor
def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
# Set the method we're using when we load the image
self.method = method
# empty list to store the source images
# loaded_images = [] <-------------------------------------------------
# load the needle image into loaded_iamges array
for img in source_images:
self.needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
loaded_images.append(self.needle_img)
return self.loaded_images <--------------------------------
def find(self, haystack_img, threshold=0.5, debug_mode=None):
for img in loaded_images:
# Save the dimensions of the needle images
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
I also wrote this next code segment that matches multiple images on a screenshot rather than a real-time image to try and figure out what I was doing wrong. It's helped me get this far but I am now stuck.
# load source images
source_images = glob.glob(r'C:\\\\\\\*.jpg')
# empty list to store the source images
loaded_images = []
for img in source_images:
needle_img = cv.imread(img, 0)
loaded_images.append(needle_img)
haystack_img = cv.imread(r'C:\\\\\\both.jpg')
haystack_img = cv.cvtColor(haystack_img, cv.COLOR_BGR2GRAY)
#loop for matching
for needles in loaded_images:
#save the dimensions of the needle images
(tH, tW) = needles.shape[:2]
result = cv.matchTemplate(haystack_img, needles, cv.TM_CCOEFF)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
top_left = max_loc
bottom_right = (top_left[0] + tW, top_left[1] + tH)
cv.rectangle(haystack_img, top_left, bottom_right, 255, 2)
cv.imshow('Result', haystack_img)
cv.waitKey(0)
Either pass loaded images directly to find:
class Search:
def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
# Set the method we're using when we load the image
self.method = method
def find(self, haystack_img, loaded_images, threshold=0.5, debug_mode=None):
for img in loaded_images:
# Save the dimensions of the needle images
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# you are overwriting the same needle_w and needle_h variable over and over again in a loop...
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
Or store loaded images in field:
class Search:
def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
# Set the method we're using when we load the image
self.method = method
self.loaded_images = []
# load the needle image into loaded_iamges array
for img in source_images:
needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
self.loaded_images.append(needle_img)
def find(self, haystack_img, threshold=0.5, debug_mode=None):
for img in self.loaded_images:
# Save the dimensions of the needle images
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# you are overwriting the same needle_w and needle_h variable over and over again in a loop...
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
In general you need to learn about scopes and difference between class and instance variables. Also I don't see here why you would need class when you would probably only have one instance of it, so maybe read about OOP in general.
You have two options in this case. Either define load_images as an instance variable with self.. But then you should call it as self.loaded_images everywhere within the class.
class Search:
self.loaded_images = []
for ...
self.loaded_images.append(...)
Or, you can define as a parameter in the function.
def find(self, loaded_images, haystack_img, threshold=0.5, debug_mode=None):
Then you can leave the definition of load_images as it is currently in __init__ method (uncomment load_images=[ ]), but be sure that you call the find function with your load_images where you had "loaded" images.
It is completely up to you how to structure your variable.