Beispiel #1
0
    def test_visible_sublanes(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)
        humorous, ig = Genre.lookup(self._db, classifier.Humorous_Fiction)

        visible_sublane = Lane(self._db, "Humorous Fiction", genres=humorous)

        visible_grandchild = Lane(self._db,
                                  "Urban Fantasy",
                                  genres=urban_fantasy)

        invisible_sublane = Lane(self._db,
                                 "Fantasy",
                                 invisible=True,
                                 genres=fantasy,
                                 sublanes=[visible_grandchild],
                                 subgenre_behavior=Lane.IN_SAME_LANE)

        lane = Lane(self._db,
                    "English",
                    sublanes=[visible_sublane, invisible_sublane],
                    subgenre_behavior=Lane.IN_SAME_LANE)

        eq_(2, len(lane.visible_sublanes))
        assert visible_sublane in lane.visible_sublanes
        assert visible_grandchild in lane.visible_sublanes
Beispiel #2
0
    def test_custom_sublanes(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        urban_fantasy, ig = Genre.lookup(self._db, classifier.Urban_Fantasy)

        urban_fantasy_lane = Lane(self._db,
                                  "Urban Fantasy",
                                  genres=urban_fantasy)

        fantasy_lane = Lane(self._db,
                            "Fantasy",
                            fantasy,
                            genres=fantasy,
                            subgenre_behavior=Lane.IN_SAME_LANE,
                            sublanes=[urban_fantasy_lane])
        eq_([urban_fantasy_lane], fantasy_lane.sublanes.lanes)

        # You can just give the name of a genre as a sublane and it
        # will work.
        fantasy_lane = Lane(self._db,
                            "Fantasy",
                            fantasy,
                            genres=fantasy,
                            subgenre_behavior=Lane.IN_SAME_LANE,
                            sublanes="Urban Fantasy")
        eq_([["Urban Fantasy"]],
            [x.genre_names for x in fantasy_lane.sublanes.lanes])
Beispiel #3
0
    def init_lanes(self, capacities):
        """
        Initialize lanes for each lane group, different lane groups might share the same lane

        :param capacities:
        :return:
        """
        capacities = [int(c) for c in capacities]
        self.capacity = sum(capacities)
        if sum(capacities) == 5:
            # if it's single lane, then all group share the same lane
            self.single_lane = True
            lane = Lane(self.link, self, max(capacities))
            self.lanes["T"].append(lane)
            self.lanes["L"].append(lane)
            self.lanes["R"].append(lane)
        else:
            # create left,right lanes
            left_lane = Lane(self.link, self, capacities[0])
            right_lane = Lane(self.link, self, capacities[-1])
            self.lanes["L"].append(left_lane)
            self.lanes["R"].append(right_lane)

            # create through lanes
            for cap in capacities[1:-1]:
                if cap:
                    self.lanes["T"].append(Lane(self.link, self, cap))

            # if capacity is 5, then left/right and through group share the same lane
            if capacities[0] == 5:
                self.lanes["T"].insert(0, left_lane)
            if capacities[-1] == 5:
                self.lanes["T"].append(right_lane)
Beispiel #4
0
    def process_image(self, image):
        img_height, img_width, _ = image.shape

        undistorted_img = self.undistort(image)
        color_binary, combined_binary = self.thresholded_binary(
            undistorted_img)
        bin_image = self.transform_to_top_down(combined_binary)

        left_fitx, right_fitx, ploty, left_fit, right_fit, leftx, lefty, rightx, righty = self.find_lane_lines(
            bin_image)
        left = Lane(leftx, lefty)
        right = Lane(rightx, righty)

        lanes = Lanes(left, right)

        self.lanes_average.update(lanes)

        if self.last_lanes is None:
            self.last_lanes = lanes

        if lanes.lanes_parallel(img_height) and lanes.distance_from_center(
            (img_width / 2, img_height)) < 4.0:
            self.last_lanes = lanes

        self.output = self.draw_overlays(
            image=image,
            left_fit=self.lanes_average.lanes.left.pixels.fit,
            right_fit=self.lanes_average.lanes.right.pixels.fit,
            leftx=self.lanes_average.left.xs,
            rightx=self.lanes_average.right.xs,
            lefty=self.lanes_average.left.ys,
            righty=self.lanes_average.right.ys)

        return self.output
Beispiel #5
0
 def test_can_change_lane_when_alone(self):
     car1 = Car(20, 0, 0, 10, 0)
     lane1 = Lane()
     lane2 = Lane()
     lane1.add_car(car1)
     light = TrafficLight(100)
     self.assertTrue(control.can_change_lane(car1, lane1, lane2, [light]))
Beispiel #6
0
 def __init__(self, *args, **kwargs):
     self.south = Lane("south")
     self.north = Lane("north")
     self.west = Lane("west")
     self.east = Lane("east")
     self.traffic_probability = self.fit_curve(False)
     self.time = 0
Beispiel #7
0
    def update_position(self, maze, modifs):
        """depending on the player answer:
        gives the conditions to update McGyver's position inside the maze.
        modifs: given by the player in the move method of class Game
        return: None if McGyver is out = True and stop reading this method"""
        if self.is_out:
            return
        # initial position = Mcgyver tile in maze
        # update position inside the maze
        pos = maze.find_tile(McGyver)
        lin = pos[0] + modifs[0]
        lin = min(lin, 14)
        lin = max(0, lin)
        col = pos[1] + modifs[1]
        col = min(col, 14)
        col = max(0, col)

        # if the new position (found with get_tile) is an item,
        # print a lane (with set_tile)
        tile = maze.get_tile(lin, col)
        if isinstance(tile, Item):
            self.inventory.append(tile.name)
            maze.set_tile(lin, col, Lane())

        # if the new position is Guardian -> check victory
        if isinstance(maze.get_tile(lin, col), Guardian):
            self.check_victory(maze)
        # if the new position is a lane: replace by mg,
        # and print lane instead of old position
        if isinstance(maze.get_tile(lin, col), Lane):
            maze.set_tile(pos[0], pos[1], Lane())
            maze.set_tile(lin, col, self)
Beispiel #8
0
 def test_cant_change_lane_when_close_to_traffic_lights(self):
     car1 = Car(98, 0, 0, 10, 0)
     lane1 = Lane()
     lane2 = Lane()
     lane1.add_car(car1)
     light = TrafficLight(100)
     self.assertFalse(control.can_change_lane(car1, lane1, lane2, [light]))
    def get_lane_type(self, img, color_img):
        right_lane = Lane()
        left_lane = Lane()

        # Detect color.
        left_lane.color, right_lane.color = detect_color(color_img)
        # Detect solid vs dotted and single vs double.
        left_lane, right_lane, left_centers, right_centers = detect_dotted(
            img, left_lane, right_lane)

        # Handle errors. Don't update lane information unless new lane has been seen for 5 frames.
        if left_lane != self.last_left:
            if self.new_count_left >= 5 or self.first_frame:
                self.last_left = left_lane
                self.new_count_left = 0
            else:
                left_lane = self.last_left
                self.new_count_left += 1
        else:
            self.new_count_left = 0
        if right_lane != self.last_right:
            if self.new_count_right >= 5 or self.first_frame:
                self.last_right = right_lane
                self.new_count_right = 0
                self.first_frame = False
            else:
                right_lane = self.last_right
                self.new_count_right += 1
        else:
            self.new_count_right = 0

        return left_lane, right_lane, left_centers, right_centers
Beispiel #10
0
def quick_detect_lane_lines(image, last_lanes):
    nonzero = image.nonzero()
    nonzero_x, nonzero_y = np.array(nonzero[1]), np.array(nonzero[0])

    last_left_p = np.poly1d(last_lanes.left.pixels.fit)
    last_right_p = np.poly1d(last_lanes.right.pixels.fit)

    margin = 100

    left_lane_indices = ((nonzero_x > (last_left_p(nonzero_y) - margin)) &
                         (nonzero_x < (last_left_p(nonzero_y) + margin)))

    right_lane_indices = ((nonzero_x > (last_right_p(nonzero_y) - margin)) &
                          (nonzero_x < (last_right_p(nonzero_y) + margin)))

    # Again, extract left and right line pixel positions
    left_x = nonzero_x[left_lane_indices]
    left_y = nonzero_y[left_lane_indices]
    right_x = nonzero_x[right_lane_indices]
    right_y = nonzero_y[right_lane_indices]

    left = Lane(left_x, left_y)
    right = Lane(right_x, right_y)

    return Lanes(left, right), image
def compute_lane_from_candidates(line_candidates, img_shape):
    # Compute lines that approximate the position of both road lanes.
    #:param line_candidates: lines from hough transform
    # :param img_shape: shape of image to which hough transform was applied
    # :return: lines that approximate left and right lane position
    pos_lines = [l for l in line_candidates if l.slope > 0]
    neg_lines = [l for l in line_candidates if l.slope < 0]

    # interpolate biases and slopes to compute equation of line that approximates
    # left lane median is employed to filter outliers
    neg_bias = np.median([l.bias for l in neg_lines]).astype(int)
    neg_slope = np.median([l.slope for l in neg_lines])
    x1, y1 = 0, neg_bias
    x2, y2 = -np.int32(np.round(neg_bias / neg_slope)), 0
    left_lane = Lane(x1, y1, x2, y2)

    # interpolate biases and slopes to compute equation of line that approximates
    # right lane median is employed to filter outliers
    lane_right_bias = np.median([l.bias for l in pos_lines]).astype(int)
    lane_right_slope = np.median([l.slope for l in pos_lines])
    x1, y1 = 0, lane_right_bias
    x2, y2 = np.int32(
        np.round(
            (img_shape[0] - lane_right_bias) / lane_right_slope)), img_shape[0]
    right_lane = Lane(x1, y1, x2, y2)

    return left_lane, right_lane
 def __init__(self):
     self.new_count_left = 0
     self.new_count_right = 0
     self.last_left = Lane()
     self.last_right = Lane()
     self.first_frame = True
     self.left_fit = None
     self.right_fit = None
Beispiel #13
0
 def test_cant_change_lane_when_car_slightly_in_front(self):
     car1 = Car(20, 0, 0, 10, 0)
     car2 = Car(25, 0, 0, 10, 0)
     lane1 = Lane()
     lane2 = Lane()
     lane1.add_car(car1)
     lane2.add_car(car2)
     light = TrafficLight(100)
     self.assertFalse(control.can_change_lane(car1, lane1, lane2, [light]))
Beispiel #14
0
 def test_shouldnt_change_lane_to_go_faster_when_nothing(self):
     car1 = Car(50, 0, 0, 10, 0)
     lane0 = Lane()
     lane1 = Lane()
     lane2 = Lane()
     lane1.add_car(car1)
     light = TrafficLight(100)
     self.assertFalse(
         control.should_change_lane_to_move_faster(car1, lane1,
                                                   [lane0, lane2], [light]))
Beispiel #15
0
 def test_can_change_lane_when_cars_around_but_far(self):
     car1 = Car(50, 0, 0, 10, 0)
     car2 = Car(20, 0, 0, 10, 0)
     car3 = Car(70, 0, 0, 10, 0)
     lane1 = Lane()
     lane2 = Lane()
     lane1.add_car(car1)
     lane2.add_car(car2)
     lane2.add_car(car3)
     light = TrafficLight(100)
     self.assertTrue(control.can_change_lane(car1, lane1, lane2, [light]))
def smoothen_over_time(lane_lines):
    # Smooth the lane line inference over a window of frames and returns the average lines
    avg_line_lt = np.zeros((len(lane_lines), 4))
    avg_line_rt = np.zeros((len(lane_lines), 4))

    for t in range(0, len(lane_lines)):
        avg_line_lt[t] += lane_lines[t][0].get_coordinates()
        avg_line_rt[t] += lane_lines[t][1].get_coordinates()

    # axis=0 : rows
    return Lane(*np.mean(avg_line_lt, axis=0)), Lane(
        *np.mean(avg_line_rt, axis=0))
Beispiel #17
0
    def test_includes_language(self):
        english_lane = Lane(self._db, self._str, languages=['eng'])
        eq_(True, english_lane.includes_language('eng'))
        eq_(False, english_lane.includes_language('fre'))

        no_english_lane = Lane(self._db, self._str, exclude_languages=['eng'])
        eq_(False, no_english_lane.includes_language('eng'))
        eq_(True, no_english_lane.includes_language('fre'))

        all_language_lane = Lane(self._db, self._str)
        eq_(True, all_language_lane.includes_language('eng'))
        eq_(True, all_language_lane.includes_language('fre'))
    def find_lane(self):
        img = self.img

        if self.last_frame and self.last_valid_left_lane_found < 12 and self.last_valid_right_lane_found < 12:
            nonzero, left_lane_inds, right_lane_inds = self._extrapolate_from_last_frame(
            )
        else:
            nonzero, left_lane_inds, right_lane_inds = self._sliding_window()

        nonzerox = np.array(nonzero[1])
        nonzeroy = np.array(nonzero[0])

        # Extract left and right line pixel positions
        leftx = nonzerox[left_lane_inds]
        lefty = nonzeroy[left_lane_inds]
        rightx = nonzerox[right_lane_inds]
        righty = nonzeroy[right_lane_inds]

        if len(leftx) == 0:
            print('no left lane')
        if len(rightx) == 0:
            print('not right lane')

        # Fit a second order polynomial to each
        left_fit_polynomial = np.polyfit(lefty, leftx, 2)
        right_fit_polynomial = np.polyfit(righty, rightx, 2)

        # Generate x and y values for plotting
        left_fitx, right_fitx, left_fity, right_fity = self._fit_polynomial_to_lane_data(
            left_fit_polynomial, right_fit_polynomial)

        left_lane = Lane(leftx, lefty, left_fit_polynomial, left_fitx,
                         left_fity)
        right_lane = Lane(rightx, righty, right_fit_polynomial, right_fitx,
                          right_fity)

        if left_lane.calculate_curvature() < 195:
            # import ipdb; ipdb.set_trace()
            left_lane = self.last_frame.left_lane if self.last_frame else None
            self.last_valid_left_lane_found += 1
        else:
            self.last_valid_left_lane_found = 0

        if right_lane.calculate_curvature() < 195:
            # import ipdb; ipdb.set_trace()
            right_lane = self.last_frame.right_lane if self.last_frame else None
            self.last_valid_right_lane_found += 1
        else:
            self.last_valid_right_lane_found = 0

        return left_lane, right_lane
Beispiel #19
0
    def __init__(self):
        self.ym_per_pix = 30 / 720  # meters per pixel in y dimension
        self.xm_per_pix = 3.7 / 880  # meters per pixel in x dimension
        self.src_points = np.float32([[240, 690], [1070, 690], [577, 460],
                                      [706, 460]])

        self.dst_points = np.float32([[200, 720], [1110, 720], [200, 25],
                                      [1110, 25]])
        self.saved_camera_calibration_path = './camera_cal/ \
                                              saved_camera_calibration.p'

        self.camera_calibration = self.__do_camera_calibration()

        self.line_left = Lane()
        self.line_right = Lane()
Beispiel #20
0
 def test_should_change_lane_to_go_to_no_car_lane(self):
     car1 = Car(20, 0, 0, 10, 0)
     car2 = Car(30, 0, 0, 10, 0)
     car3 = Car(60, 0, 0, 10, 0)
     lane0 = Lane()
     lane1 = Lane()
     lane2 = Lane()
     lane1.add_car(car1)
     lane1.add_car(car3)
     lane0.add_car(car2)
     light = TrafficLight(100)
     self.assertEquals(
         control.should_change_lane_to_move_faster(car1, lane1,
                                                   [lane0, lane2], [light]),
         lane2)
def process_video_file(file_name, camera):
    lane_object = Lane(camera.image_shape)

    src_clip = VideoFileClip(file_name)
    dst_clip = src_clip.fl_image(
        lambda frame: process_image(frame, camera, lane_object))
    dst_clip.write_videofile(get_out_file_name(file_name), audio=False)
Beispiel #22
0
 def test_shouldnt_change_lane_when_current_lane_is_fastest(self):
     car0 = Car(30, 0, 0, 10, 0)
     car1 = Car(20, 0, 0, 10, 0)
     car2 = Car(45, 0, 0, 10, 0)
     car3 = Car(50, 0, 0, 10, 0)
     lane0 = Lane()
     lane1 = Lane()
     lane2 = Lane()
     lane0.add_car(car0)
     lane1.add_car(car1)
     lane1.add_car(car3)
     lane2.add_car(car2)
     light = TrafficLight(100)
     self.assertFalse(
         control.should_change_lane_to_move_faster(car1, lane1,
                                                   [lane0, lane2], [light]))
    def test_refusal_to_create_expensive_feed(self):

        facets = Facets.default()
        pagination = Pagination.default()
        lane = Lane(self._db, "My Lane", languages=['eng', 'chi'])

        args = (self._db, lane, CachedFeed.PAGE_TYPE, facets, pagination, None)

        # If we ask for a group feed that will be cached forever, and it's
        # not around, we'll get a page feed instead.
        feed, fresh = CachedFeed.fetch(*args,
                                       max_age=Configuration.CACHE_FOREVER)
        eq_(CachedFeed.PAGE_TYPE, feed.type)

        # If we ask for the same feed, but we don't say it must be cached
        # forever, it'll be created.
        feed, fresh = CachedFeed.fetch(*args, max_age=0)

        # Or if we explicitly demand that the feed be created, it will
        # be created.
        feed, fresh = CachedFeed.fetch(*args,
                                       force_refresh=True,
                                       max_age=Configuration.CACHE_FOREVER)
        feed.update("Cache this forever!")

        # Once the feed has content associated with it, we can ask for
        # it in cached-forever mode and no longer get the exception.
        feed, fresh = CachedFeed.fetch(*args,
                                       max_age=Configuration.CACHE_FOREVER)
        eq_("Cache this forever!", feed.content)
    def test_lifecycle(self):
        facets = Facets.default()
        pagination = Pagination.default()
        lane = Lane(self._db, "My Lane", languages=['eng', 'chi'])

        # Fetch a cached feed from the database--it's empty.
        args = (self._db, lane, CachedFeed.PAGE_TYPE, facets, pagination, None)
        feed, fresh = CachedFeed.fetch(*args, max_age=0)

        eq_(False, fresh)
        eq_(None, feed.content)

        eq_(pagination.query_string, feed.pagination)
        eq_(facets.query_string, feed.facets)
        eq_(lane.name, feed.lane_name)
        eq_('eng,chi', feed.languages)

        # Update the content
        feed.update("The content")
        self._db.commit()

        # Fetch it again.
        feed, fresh = CachedFeed.fetch(*args, max_age=0)

        # Now it's cached! But not fresh, because max_age is zero
        eq_("The content", feed.content)
        eq_(False, fresh)

        # Lower our standards, and it's fresh!
        feed, fresh = CachedFeed.fetch(*args, max_age=1000)
        eq_("The content", feed.content)
        eq_(True, fresh)
Beispiel #25
0
    def test_query_works_from_lane_definition_handles_exclude_languages(self):
        search = DummyExternalSearchIndex()

        lane = Lane(
            self._db,
            self._default_library,
            "Not english or spanish",
            exclude_languages=set(['eng', 'spa']),
        )
        filter = search.make_filter(
            lane.media,
            lane.languages,
            lane.exclude_languages,
            lane.fiction,
            list(lane.audiences),
            lane.age_range,
            lane.genre_ids,
        )

        exclude_languages_filter, medium_filter = filter['and']
        expect_exclude_languages = ['eng', 'spa']
        assert 'not' in exclude_languages_filter
        assert 'terms' in exclude_languages_filter['not']
        assert 'language' in exclude_languages_filter['not']['terms']
        eq_(expect_exclude_languages,
            sorted(exclude_languages_filter['not']['terms']['language']))
Beispiel #26
0
    def __init__(self):

        self.image_shape = [0, 0]

        self.camera_calibration_path = '../camera_cal/'
        self.output_images_path = '../output_images/'
        self.input_video_path = '../input_video/'
        self.output_video_path = '../output_video/'

        self.sobel_kernel_size = 7
        self.sx_thresh = (60, 255)
        self.sy_thresh = (60, 150)
        self.s_thresh = (170, 255)
        self.mag_thresh = (40, 255)
        self.dir_thresh = (.65, 1.05)

        self.wrap_src = np.float32([[595, 450], [686, 450], [1102, 719], [206, 719]])
        self.wrap_dst = np.float32([[320, 0], [980, 0], [980, 719], [320, 719]])

        self.mask_offset = 30
        self.vertices = [np.array([[206-self.mask_offset, 719],
                                   [595-self.mask_offset, 460-self.mask_offset],
                                   [686+self.mask_offset, 460-self.mask_offset],
                                   [1102+self.mask_offset, 719]],
                                  dtype=np.int32)]

        self.mask_offset_inverse = 30
        self.vertices_inverse = [np.array([[206+self.mask_offset_inverse, 719],
                                   [595+self.mask_offset_inverse, 460-self.mask_offset_inverse],
                                   [686-self.mask_offset_inverse, 460-self.mask_offset_inverse],
                                   [1102-self.mask_offset_inverse, 719]],
                                  dtype=np.int32)]

        self.thresh = Threshold()
        self.lane = Lane()
Beispiel #27
0
    def test_lane_query_with_configured_opds(self):
        """The appropriate opds entry is deferred during querying.
        """
        original_setting = Configuration.DEFAULT_OPDS_FORMAT
        lane = Lane(self._db, "Everything")

        # Verbose config doesn't query simple OPDS entries.
        Configuration.DEFAULT_OPDS_FORMAT = "verbose_opds_entry"
        works_query_str = str(lane.works())
        mw_query_str = str(lane.materialized_works())

        assert "verbose_opds_entry" in works_query_str
        assert "verbose_opds_entry" in mw_query_str
        assert "works.simple_opds_entry" not in works_query_str
        assert "simple_opds_entry" not in mw_query_str

        # Simple config doesn't query verbose OPDS entries.
        Configuration.DEFAULT_OPDS_FORMAT = "simple_opds_entry"
        works_query_str = str(lane.works())
        mw_query_str = str(lane.materialized_works())

        assert "works.simple_opds_entry" in works_query_str
        assert "simple_opds_entry" in mw_query_str
        assert "verbose_opds_entry" not in works_query_str
        assert "verbose_opds_entry" not in mw_query_str

        Configuration.DEFAULT_OPDS_FORMAT = original_setting
Beispiel #28
0
    def test_get_search_target(self):
        fantasy, ig = Genre.lookup(self._db, classifier.Fantasy)
        lane = Lane(
            self._db, "YA Fantasy", genres=fantasy, 
            languages='eng',
            audiences=Lane.AUDIENCE_YOUNG_ADULT,
            age_range=[15,16],
            subgenre_behavior=Lane.IN_SUBLANES
        )
        sublanes = lane.sublanes.lanes
        names = sorted([x.name for x in sublanes])
        eq_(["Epic Fantasy", "Historical Fantasy", "Urban Fantasy"],
            names)

        # To start with, none of the lanes are searchable.
        eq_(None, lane.search_target)
        eq_(None, sublanes[0].search_target)

        # If we make a lane searchable, suddenly there's a search target.
        lane.searchable = True
        eq_(lane, lane.search_target)

        # The searchable lane also becomes the search target for its
        # children.
        eq_(lane, sublanes[0].search_target)
Beispiel #29
0
    def test_staff_picks_and_best_sellers_sublane(self):
        staff_picks, ignore = self._customlist(
            foreign_identifier=u"Staff Picks",
            name=u"Staff Picks!",
            data_source_name=DataSource.LIBRARY_STAFF,
            num_entries=0)
        best_sellers, ignore = self._customlist(
            foreign_identifier=u"NYT Best Sellers",
            name=u"Best Sellers!",
            data_source_name=DataSource.NYT,
            num_entries=0)
        lane = Lane(self._db,
                    "Everything",
                    include_staff_picks=True,
                    include_best_sellers=True)

        # A staff picks sublane and a best-sellers sublane have been
        # created for us.
        best, picks = lane.sublanes.lanes
        eq_("Best Sellers", best.display_name)
        eq_("Everything - Best Sellers", best.name)
        nyt = DataSource.lookup(self._db, DataSource.NYT)
        eq_(nyt.id, best.list_data_source_id)

        eq_("Staff Picks", picks.display_name)
        eq_("Everything - Staff Picks", picks.name)
        eq_([staff_picks.id], picks.list_ids)
def process_image(file_name, path, show):
    """
    Loads a given image path, and applies the pipeline to it

    :param file_name: The name of the file (without extension)
    :param path: Path to the input (image, video) to process.
    :param show: Show the outcome

    :return: None
    """

    # load the image
    image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    height, width = image.shape[:2]

    # initialize the objects
    threshold = Threshold()
    lane = Lane(height=height)
    camera = Camera(image_size=(width, height))
    transform = Transform(width=width, height=height)

    # process the frame
    output = pipeline(image, camera, threshold, transform, lane)

    # save the output
    cv2.imwrite(f'data/processed/output_images/{file_name}.jpg', output)