class VideoProcessor:
    """
    Class used to process a video file and that produces an annotated version with the detected lanes.
    """

    def __init__(self, calibration_data_file, smooth_frames = 5, debug = False, failed_frames_dir = 'failed_frames'):
        self.img_processor = ImageProcessor(calibration_data_file)
        self.lane_tracker = LaneDetector(smooth_frames = smooth_frames)
        self.frame_count = 0
        self.fail_count = 0
        self.debug = debug
        self.failed_frames_dir = failed_frames_dir
        self.last_frame = None
        self.processed_frames = None

    def process_frame(self, img):

        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
       
        undistorted_img, thresholded_img, warped_img = self.img_processor.process_image(img)
        
        _, polyfit, curvature, deviation, fail_code = self.lane_tracker.detect_lanes(warped_img)
        
        fill_color = (0, 255, 0) if fail_code == 0 else (0, 255, 255)

        lane_img = self.lane_tracker.draw_lanes(undistorted_img, polyfit, fill_color = fill_color)
        lane_img = self.img_processor.unwarp_image(lane_img)

        out_image = cv2.addWeighted(undistorted_img, 1.0, lane_img, 1.0, 0)

        font = cv2.FONT_HERSHEY_DUPLEX
        font_color = (0, 255, 0)

        curvature_text = 'Left Curvature: {:.1f}, Right Curvature: {:.1f}'.format(curvature[0], curvature[1])
        offset_text = 'Center Offset: {:.2f} m'.format(deviation)

        cv2.putText(out_image, curvature_text, (30, 60), font, 1, font_color, 2)
        cv2.putText(out_image, offset_text, (30, 90), font, 1, font_color, 2)

        if fail_code > 0:
            self.fail_count += 1
            failed_text = 'Detection Failed: {}'.format(LaneDetector.FAIL_CODES[fail_code])
            cv2.putText(out_image, failed_text, (30, 120), font, 1, (0, 0, 255), 2)
            if self.debug:
                print(failed_text)
                cv2.imwrite(os.path.join(self.failed_frames_dir, 'frame' + str(self.frame_count) + '_failed_' + str(fail_code) + '.png'), img)
                cv2.imwrite(os.path.join(self.failed_frames_dir, 'frame' + str(self.frame_count) + '_failed_' + str(fail_code) + '_lanes.png'), out_image)
                if self.last_frame is not None: # Saves also the previous frame for comparison
                    cv2.imwrite(os.path.join(self.failed_frames_dir, 'frame' + str(self.frame_count) + '_failed_' + str(fail_code) + '_prev.png'), self.last_frame)

        self.frame_count += 1
        self.last_frame = img

        out_image = cv2.cvtColor(out_image, cv2.COLOR_BGR2RGB)

        return out_image

    def process_frame_split(self, img):

        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
       
        undistorted_img, thresholded_img, processed_img = self.img_processor.process_image(img)

        lanes_centroids, polyfit, curvature, deviation, fail_code = self.lane_tracker.detect_lanes(processed_img)
        
        fill_color = (0, 255, 0) if fail_code == 0 else (0, 255, 255)

        lane_img = self.lane_tracker.draw_lanes(undistorted_img, polyfit, fill_color = fill_color)
        lane_img = self.img_processor.unwarp_image(lane_img)

        out_image = cv2.addWeighted(undistorted_img, 1.0, lane_img, 1.0, 0)

        color_img = self.img_processor.color_thresh(undistorted_img) * 255
        gradient_img = self.img_processor.gradient_thresh(undistorted_img) * 255
      
        processed_src = np.array(cv2.merge((thresholded_img, thresholded_img, thresholded_img)), np.uint8)
        src, _ = self.img_processor._warp_coordinates(img)

        src = np.array(src, np.int32)
        
        cv2.polylines(processed_src, [src], True, (0,0,255), 2)

        window_img = np.copy(processed_img)

        window_img = self.lane_tracker.draw_windows(window_img, lanes_centroids, polyfit, blend = True)

        lanes_img = np.copy(out_image)

        font = cv2.FONT_HERSHEY_DUPLEX
        font_color = (0, 255, 0)

        curvature_text = 'Left Curvature: {:.1f}, Right Curvature: {:.1f}'.format(curvature[0], curvature[1])
        offset_text = 'Center Offset: {:.2f} m'.format(deviation)

        cv2.putText(out_image, curvature_text, (30, 60), font, 1, font_color, 2)
        cv2.putText(out_image, offset_text, (30, 90), font, 1, font_color, 2)

        if fail_code > 0:
            self.fail_count += 1
            failed_text = 'Detection Failed: {}'.format(LaneDetector.FAIL_CODES[fail_code])
            cv2.putText(out_image, failed_text, (30, 120), font, 1, (0, 0, 255), 2)
            if self.debug:
                print(failed_text)
                cv2.imwrite(os.path.join(self.failed_frames_dir, 'frame' + str(self.frame_count) + '_failed_' + str(fail_code) + '.png'), img)
                cv2.imwrite(os.path.join(self.failed_frames_dir, 'frame' + str(self.frame_count) + '_failed_' + str(fail_code) + '_lanes.png'), out_image)
                if self.last_frame is not None: # Saves also the previous frame for comparison
                    cv2.imwrite(os.path.join(self.failed_frames_dir, 'frame' + str(self.frame_count) + '_failed_' + str(fail_code) + '_prev.png'), self.last_frame)

        self.frame_count += 1

        undistorted_img = cv2.cvtColor(undistorted_img, cv2.COLOR_BGR2RGB)
        color_img = cv2.cvtColor(color_img, cv2.COLOR_GRAY2RGB)
        gradient_img = cv2.cvtColor(gradient_img, cv2.COLOR_GRAY2RGB)
        thresholded_img= cv2.cvtColor(thresholded_img, cv2.COLOR_GRAY2RGB)
        processed_src= cv2.cvtColor(processed_src, cv2.COLOR_BGR2RGB)
        processed_img= cv2.cvtColor(processed_img, cv2.COLOR_GRAY2RGB)
        window_img= cv2.cvtColor(window_img, cv2.COLOR_BGR2RGB) 
        lanes_img= cv2.cvtColor(lanes_img, cv2.COLOR_BGR2RGB)
        out_image= cv2.cvtColor(out_image, cv2.COLOR_BGR2RGB)

        return (undistorted_img, color_img, gradient_img, thresholded_img, processed_src, processed_img, window_img, lanes_img, out_image)

    def process_video(self, file_path, output, t_start = None, t_end = None):

        input_clip = VideoFileClip(file_path)
        
        if t_start is not None:
            input_clip = input_clip.subclip(t_start = t_start, t_end = t_end)
        
        output_clip = input_clip.fl_image(self.process_frame)
        output_clip.write_videofile(output, audio = False)

    def process_frame_stage(self, img, idx):
        
        if self.processed_frames is None:
            self.processed_frames = []

        if idx == 0:
            result = self.process_frame_split(img)
            self.processed_frames.append(result)
        else:
            self.frame_count += 1

        return self.processed_frames[self.frame_count - 1][idx]

    def process_video_split(self, file_path, output, t_start = None, t_end = None):

        input_clip = VideoFileClip(file_path)
        
        if t_start is not None:
            input_clip = input_clip.subclip(t_start = t_start, t_end = t_end)

        out_file_prefix = os.path.split(output)[1][:-4]
        
        idx = 0
        output_clip = input_clip.fl_image(lambda img: self.process_frame_stage(img, idx))
        output_clip.write_videofile(out_file_prefix + '_undistoreted.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_color.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_gradient.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_thresholded.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_processed_src.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_processed_dst.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_window.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_lanes.mp4', audio = False)
        idx += 1
        self.frame_count = 0
        output_clip.write_videofile(out_file_prefix + '_final.mp4', audio = False)

        print('Number of failed detection: {}'.format(self.fail_count))
Exemple #2
0
        cv2.polylines(processed_dst, [dst], True, (0, 0, 255), 2)

        cv2.imwrite(out_file_prefix + '_persp_src.jpg', processed_src)
        cv2.imwrite(out_file_prefix + '_persp_dst.jpg', processed_dst)

        lanes_centroids, polyfit, curvature, deviation, fail_code = lane_tracker.detect_lanes(
            processed_img)
        processed_img = lane_tracker.draw_windows(processed_img,
                                                  lanes_centroids,
                                                  polyfit,
                                                  blend=True)

        cv2.imwrite(out_file_prefix + '_windows.jpg', processed_img)

        lanes_img = lane_tracker.draw_lanes(input_img, polyfit)
        lanes_img = img_processor.unwarp_image(lanes_img)

        lanes_img = cv2.addWeighted(undistorted_img, 1.0, lanes_img, 1.0, 0)
        font = cv2.FONT_HERSHEY_DUPLEX
        font_color = (0, 255, 0)
        cv2.putText(
            lanes_img,
            'Left Curvature: {:.1f}, Right Curvature: {:.1f}'.format(
                curvature[0], curvature[1]), (30, 60), font, 1, font_color, 2)
        cv2.putText(lanes_img, 'Center Offset: {:.2f} m'.format(deviation),
                    (30, 90), font, 1, font_color, 2)

        if fail_code > 0:
            failed_text = 'Detection Failed: {}'.format(
                LaneDetector.FAIL_CODES[fail_code])
            print(failed_text)