Joanna
Joanna

Reputation: 69

For loop function for image processing and saving causing an error - Expected Ptr<cv::UMat> for argument '%s'

I'm working on lane detection on images. I a have a function that takes a path to an image and returns an image with lanes detected. I need to write another function thae takes a path to a directory with multiple images and output path as input, uses the first function to process an image and then save it in an output directory.

This is the code from the beginning, where I define helper functions if you want to reproduce it:

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import cv2
%matplotlib inline

def to_gray(image):
    gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
    return gray

def blur_gray(gray):
    kernel_size = 5
    blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
    return blur_gray

def Canny(blur_gray):
    low_threshold = 100
    high_threshold = 200
    edges = cv2.Canny(blur_gray, low_threshold, high_threshold)
    return edges

def masked_edges(image, edges):
    mask = np.zeros_like(edges)   
    ignore_mask_color = 255  
    imshape = image.shape
    vertices = np.array([[(130,imshape[0]),(420, 325), (540,325), (850,imshape[0])]], dtype=np.int32)
    cv2.fillPoly(mask, vertices, ignore_mask_color)
    masked_edges = cv2.bitwise_and(edges, mask)
    return masked_edges

def lines (masked_edges, image):
    # Define the Hough transform parameters
    # Make a blank the same size as our image to draw on
    rho = 1 # distance resolution in pixels of the Hough grid
    theta = np.pi/180 # angular resolution in radians of the Hough grid
    threshold = 1     # minimum number of votes (intersections in Hough grid cell)
    min_line_length = 3 #minimum number of pixels making up a line
    max_line_gap = 4    # maximum gap in pixels between connectable line segments
    line_image = np.copy(image)*0 # creating a blank to draw lines on

    # Run Hough on edge detected image
    # Output "lines" is an array containing endpoints of detected line segments
    lines = cv2.HoughLinesP(masked_edges, rho, theta, threshold, np.array([]),
                                min_line_length, max_line_gap)
    return lines

def draw_lines(lines, image, edges):
    line_image = np.copy(image)*0 # creating a blank to draw lines o
    # Iterate over the output "lines" and draw lines on a blank image
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)

    # Create a "color" binary image to combine with line image
    color_edges = np.dstack((edges, edges, edges)) 

    # Draw the lines on the edge image
    lines_edges = cv2.addWeighted(color_edges, 0.8, line_image, 1, 0) 
    pic_lanes = plt.imshow(lines_edges)
    return pic_lanes

So this is the function that incorporates multiple helper functions from above and returns a single image:

def image_pipeline(image_path):
  image = mpimg.imread(image_path)
  gray = to_gray(image)
  to_blur_gray = blur_gray(gray)
  edges = Canny(to_blur_gray)
  masked = masked_edges(image, edges)
  line = lines(masked, image)
  pic_lanes = draw_lines(line, image, edges)
  return pic_lanes

A picture returned by the function above

Then I want to use the above function within a loop function that would do the same for all the images from an input directory and save them in an output directory.

def video_loop(outPath, path):
  image_no = 1
  for image_path in os.listdir(path):
        
        # create the full input path and read the file
        input_path = os.path.join(path, image_path)
        pic_lanes = image_pipeline(input_path)
        fullpath = os.path.join(outPath, image_path)
        name = fullpath + '.jpg'
        cv2.imwrite(name, pic_lanes)
        os.chdir(fullpath)
        image_no += 1
if __name__ == '__video_loop__':
    video_loop()
out= '/content/CarND-LaneLines-P1/Solid White Frames Canny'
path1 = '/content/CarND-LaneLines-P1/Frames Solid White Right'
video_loop(out, path1)

Unfortunately I get an error together with one returned image:

TypeError                                 Traceback (most recent call last)
<ipython-input-109-10dea7ed1446> in <module>()
      1 out= '/content/CarND-LaneLines-P1/Solid White Frames Canny'
      2 path1 = '/content/CarND-LaneLines-P1/Frames Solid White Right'
----> 3 video_loop(out, path1)

<ipython-input-108-a52f4438bc0d> in video_loop(outPath, path)
      8         fullpath = os.path.join(outPath, image_path)
      9         name = fullpath + '.jpg'
---> 10         cv2.imwrite(name, pic_lanes)
     11         os.chdir(fullpath)
     12         image_no += 1

TypeError: Expected Ptr<cv::UMat> for argument '%s'

I have searched for the meaning of this error and someone suggested that the cv2.imwrite() doesn't get a valid argument (the picture doesn't exist) but I'm not sure how to fix this.

EDIT: I also tried something simpler like this:

count=0
for filename in os.listdir('/content/CarND-LaneLines-P1/Frames Solid White Right'):
  detected_lanes = image_pipeline(filename)
  detected_lanes = cv2.imread(detected_lanes)
  cv2.imwrite(filename, detected_lanes)
  os.chdir('/content/CarND-LaneLines-P1/Frames Solid White Right/Canny')
  count =+1

But i'm gettin a different error here:

SystemError                               Traceback (most recent call last)
<ipython-input-15-1d3fff5ab2bb> in <module>()
      2 for filename in os.listdir('/content/CarND-LaneLines-P1/Frames Solid White Right'):
      3   detected_lanes = image_pipeline(filename)
----> 4   detected_lanes = cv2.imread(detected_lanes)
      5   cv2.imwrite(filename, detected_lanes)
      6   os.chdir('/content/CarND-LaneLines-P1/Frames Solid White Right/Canny')

SystemError: <built-in function imread> returned NULL without setting an error

I don't know how to approach this. If you want to run this code, just use the code provided and then use image_pipeline and pass it any image.

Any chance you could help me out here?

Upvotes: 0

Views: 433

Answers (1)

Joanna
Joanna

Reputation: 69

I have managed to solve my problem by passing a save function within my pipeline and only then ran a loop. First I changed draw_lines function to also save the image:

def draw_lines(lines, image, edges, image_path, path_to_save_files):
    copy = np.copy(image)
    line_image = np.copy(image)*0 # creating a blank to draw lines o
    # Iterate over the output "lines" and draw lines on a blank image
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)

    # Create a "color" binary image to combine with line image
    color_edges = np.dstack((edges, edges, edges)) 
    # Draw the lines on the edge image
    my_dpi=96
    lines_edges = cv2.addWeighted(color_edges, 0.8, line_image, 1, 0) 
    final = cv2.addWeighted(lines_edges, 0.5, image, 0.7, 50)
  
    plt.figure(figsize=(960/my_dpi, 540/my_dpi), dpi=my_dpi)
    final_image = plt.imshow(final)
    plt.axis('off')
    save_fname = os.path.join(outpath, os.path.basename(image_path))
    plt.savefig(save_fname, bbox_inches='tight', pad_inches=0, transparent=True)  

I used plt.savefig() by passing it save_fname that changes accordingly with every image (it takes the path to the directory where I want to store my images together with just the name of the file derived from its original path). Additionally, I didn't want my picture to be in a form of a matrix so I used plt.axis('off').

I then used a simple for loop:

count= 0
  for file in os.listdir(path_with_images):
    image_pipeline(file, path_to_save_files)
    count=+1

This works great for me.

Upvotes: 1

Related Questions