\[ \begin{align}\begin{aligned}\newcommand\blank{~\underline{\hspace{1.2cm}}~}\\% Bold symbols (vectors) \newcommand\bs[1]{\mathbf{#1}}\\% Differential \newcommand\dd[2][]{\mathrm{d}^{#1}{#2}} % use as \dd, \dd{x}, or \dd[2]{x}\\% Poor man's siunitx \newcommand\unit[1]{\mathrm{#1}} \newcommand\num[1]{#1} \newcommand\qty[2]{#1~\unit{#2}}\\\newcommand\per{/} \newcommand\squared{{}^2} \newcommand\cubed{{}^3} % % Scale \newcommand\milli{\unit{m}} \newcommand\centi{\unit{c}} \newcommand\kilo{\unit{k}} \newcommand\mega{\unit{M}} % % Percent \newcommand\percent{\unit{{\kern-4mu}\%}} % % Angle \newcommand\radian{\unit{rad}} \newcommand\degree{\unit{{\kern-4mu}^\circ}} % % Time \newcommand\second{\unit{s}} \newcommand\s{\second} \newcommand\minute{\unit{min}} \newcommand\hour{\unit{h}} % % Distance \newcommand\meter{\unit{m}} \newcommand\m{\meter} \newcommand\inch{\unit{in}} \newcommand\foot{\unit{ft}} % % Force \newcommand\newton{\unit{N}} \newcommand\kip{\unit{kip}} % kilopound in "freedom" units - edit made by Sri % % Mass \newcommand\gram{\unit{g}} \newcommand\g{\gram} \newcommand\kilogram{\unit{kg}} \newcommand\kg{\kilogram} \newcommand\grain{\unit{grain}} \newcommand\ounce{\unit{oz}} \newcommand\pound{\unit{lbs}} % % Temperature \newcommand\kelvin{\unit{K}} \newcommand\K{\kelvin} \newcommand\celsius{\unit{{\kern-4mu}^\circ C}} \newcommand\C{\celsius} \newcommand\fahrenheit{\unit{{\kern-4mu}^\circ F}} \newcommand\F{\fahrenheit} % % Area \newcommand\sqft{\unit{sq\,\foot}} % square foot % % Volume \newcommand\liter{\unit{L}} \newcommand\gallon{\unit{gal}} % % Frequency \newcommand\hertz{\unit{Hz}} \newcommand\rpm{\unit{rpm}} % % Voltage \newcommand\volt{\unit{V}} \newcommand\V{\volt} \newcommand\millivolt{\milli\volt} \newcommand\mV{\milli\volt} \newcommand\kilovolt{\kilo\volt} \newcommand\kV{\kilo\volt} % % Current \newcommand\ampere{\unit{A}} \newcommand\A{\ampere} \newcommand\milliampereA{\milli\ampere} \newcommand\mA{\milli\ampere} \newcommand\kiloampereA{\kilo\ampere} \newcommand\kA{\kilo\ampere} % % Resistance \newcommand\ohm{\Omega} \newcommand\milliohm{\milli\ohm} \newcommand\kiloohm{\kilo\ohm} % correct SI spelling \newcommand\kilohm{\kilo\ohm} % "American" spelling used in siunitx \newcommand\megaohm{\mega\ohm} % correct SI spelling \newcommand\megohm{\mega\ohm} % "American" spelling used in siunitx % % Capacitance \newcommand\farad{\unit{F}} \newcommand\F{\farad} \newcommand\microfarad{\micro\farad} \newcommand\muF{\micro\farad} % % Inductance \newcommand\henry{\unit{H}} \newcommand\H{\henry} \newcommand\millihenry{\milli\henry} \newcommand\mH{\milli\henry} % % Power \newcommand\watt{\unit{W}} \newcommand\W{\watt} \newcommand\milliwatt{\milli\watt} \newcommand\mW{\milli\watt} \newcommand\kilowatt{\kilo\watt} \newcommand\kW{\kilo\watt} % % Energy \newcommand\joule{\unit{J}} \newcommand\J{\joule} % % Composite units % % Torque \newcommand\ozin{\unit{\ounce}\,\unit{in}} \newcommand\newtonmeter{\unit{\newton\,\meter}} % % Pressure \newcommand\psf{\unit{psf}} % pounds per square foot \newcommand\pcf{\unit{pcf}} % pounds per cubic foot \newcommand\pascal{\unit{Pa}} \newcommand\Pa{\pascal} \newcommand\ksi{\unit{ksi}} % kilopound per square inch \newcommand\bar{\unit{bar}} \end{aligned}\end{align} \]

Apr 28, 2026 | 919 words | 9 min read

6.2.2. Task 2#

Learning Objectives#

  • Extract color features from an image

  • Create a dataset of these extracted features

  • Use the pandas library to read data, apply functions, and build a final feature dataset.

Introduction#

In this task, you will build a complete data processing pipeline that reads a CSV metadata file, processes each image it references, and produces an analysis-ready feature dataset. For every image the pipeline will clean the raw pixel data, convert it to the HSV color space, and compute statistical and shape-based features. The resulting dataset will later be used to train and test your machine learning models.

The pipeline relies on functions you implemented in Section 5.2.3 and Section 6.2.1, so you will reuse that work here.

This task will also require you to use the pandas library, which is a powerful tool for tabular data manipulation and analysis in Python. If you are unfamiliar with pandas, refer to the materials provided in Section 6.1.1.

Task Instructions#

Deliverable Reminder

Create a flowchart of your algorithm and save it as tp2_team_2_teamnumber.pdf. Start your program from a copy of the ENGR133_Python_Template.py template and name it tp2_team_2_teamnumber.py.

Note

This task reuses several functions from earlier assignments. Copy the following into your program:

Extract Features Function#

Create a function named extract_features with the following input argument:

  • img_array: A NumPy array representing a resized and padded RGB image (100x100x3)

The function should extract nine features from the image and return them as a list. The six HSV statistics are floats; number_lines, has_circle, and has_triangle are ints. To compute these features, follow these steps:

  1. Convert the input RGB image to the HSV color space using the convert_to_hsv function you developed previously.

  2. For each of the three channels of the HSV image, calculate its mean and standard deviation. This will give you six features: hue_mean, hue_std, saturation_mean, saturation_std, value_mean, and value_std. Use numpy functions mentioned in Common NumPy array methods.

  3. Convert the RGB image to grayscale for extracting shape-based features. Using the grayscale image and the functions provided below, detect whether a circle is present and count the number of lines present in the image. This will give you three additional features:

    • number_lines (the count of lines detected).

    • has_circle (1 if a circle is detected, 0 otherwise).

    • has_triangle (1 if a triangle is detected, 0 otherwise).

    Note

    Code Snippet: The partially completed functions below demonstrate how to detect circles, triangles and count the number of lines given a grayscale image. You will need to use the gaussian_filter and sobel_filter functions from the previous tasks to fill in the TODO sections.

    Read more about OpenCV in Section 14.1.1.

    import cv2
    
    def detect_circle(gray_img):
        """Detects if a large circle is present. Returns 1 if found, 0 otherwise."""
        # Hough Circles works best on a grayscale, slightly blurred image
    
        blurred_img = #TODO: Use gaussian_filter (from the previous checkpoint) with sigma=1.5 to blur the input grayscale image
        blurred_img_array = np.array(blurred_img)  # Convert to NumPy array for processing with cv2
    
        # Detect circles
        circles = cv2.HoughCircles(
            blurred_img,
            cv2.HOUGH_GRADIENT,
            dp=1.2,  # Inverse ratio of accumulator resolution
            minDist=50,  # Minimum distance between centers of detected circles
            param1=100,  # Upper threshold for the internal Canny edge detector
            param2=30,  # Threshold for center detection
            minRadius=20,  # Minimum circle radius to detect
            maxRadius=50  # Maximum circle radius to detect
        )
    
        return #TODO: return 1 if any circles are detected, otherwise return 0
    
    def detect_triangle(gray_img):
        """Detects if a triangle is present. Returns 1 if found, 0 otherwise."""
        blurred = gaussian_filter(gray_img, sigma=1.0)
        edges = sobel_filter(blurred)
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
        for contour in contours:
            epsilon = 0.04 * cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, epsilon, True)
            if len(approx) == 3 and cv2.contourArea(approx) > 625:
                return 1
        return 0
    
    def count_lines(gray_img):
        """Counts the number of lines in an image using Hough Line Transform."""
        # Edge detection via sobel filtering is a prerequisite for Hough Lines
        edges = #TODO: Use sobel_filter (from Task 1) on the input grayscale image
    
        # Detect lines using the edge map
        lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=50, minLineLength=30, maxLineGap=10)
    
        return #TODO: return the number of lines detected, if no lines are detected return 0
    
  4. Finally, your function should return these nine calculated values, in the following order as a list: [hue_mean, hue_std, saturation_mean, saturation_std, value_mean, value_std, number_lines, has_circle, has_triangle]

Main Function#

Create a main function that orchestrates the entire data processing pipeline.

  1. It should first collect the following inputs from the user:

    • the path to the image dataset folder

    • the name of the metadata CSV file (this is the file which stores the path and class label for each image)

    • the name of the output CSV file to save the dataset of extracted features

  2. The function should load the metadata CSV file into a pandas DataFrame using pd.read_csv().

  3. Print the head of this DataFrame to verify it loaded correctly.

  4. Process each row of the DataFrame. For each row:

    • Extract the image path from the appropriate column in the DataFrame.

    • Load the image using the load_img function you created previously.

    • Rescale the NumPy array returned to uint8 format in the range \([0, 255]\) as you did in Task 3.

    • Resize and pad the image to \(100 \times 100\) using the resize_image, and pad_image functions you created previously.

    • Extract the nine features using the extract_features function you created above.

    • Store these features along with the image path and its class ID in a new DataFrame (with \(9 + 2 = 11\) columns).

    • Store this new DataFrame as a CSV file with the output name specified by the user.

    Note

    Once you get to this stage, you may realize that the feature extraction process is slow. This is because image processing is computationally intensive and you are processing one image at a time.

    Instead of recomputing the features every time you run your script, you can save the dataset of extracted features to a CSV file. Then, in future runs, you can simply load this CSV file directly if it exists. You may find Pathlib useful for checking if a file exists.

    This approach is common in data science workflows to save time and computational resources.

    Alternatively, you can just process the first 5 images and compare the extracted feature values to the sample output provided below.

    Hint

    (OPTIONAL) You may find the pandas.DataFrame.apply() function useful for applying the feature extraction function to each row of the metadata DataFrame. Read the docs if you wish to use this function.

  5. Print the shape and the head of the final dataset to show the result.

  6. Prompt the user for the path of a specific image from the dataset.

  7. Print out the features for this image by filtering the DataFrame for the row corresponding to this image path.

Use the files provided in the Table 6.6. Ensure that your code output matches the sample output provided below when using the same input image paths.

Table 6.6 Dataset#

File Name

Description

ML_Images.zip

Zip file containing all of the training & testing images

img_metadata.csv

This file contains class labels (0 = Deer Warning & 1 = Left Turn Ahead) for each image

Sample Output#

Use the values in Table 6.7 below to test your program.

Table 6.7 Test Cases#

Case

dataset

metadata

output_path

image_path

1

ML_Images

img_metadata.csv

img_features.csv

0076.png

2

ML_Images

img_metadata.csv

img_features.csv

0999.png

3

ML_Images

img_metadata.csv

img_features.csv

0176.png

4

ML_Images

img_metadata.csv

img_features.csv

0761.png

Ensure your program’s output matches the provided samples exactly. This includes all characters, white space, and punctuation. In the samples, user input is highlighted like this for clarity, but your program should not highlight user input in this way.

Case 1 Sample Output

$ python3 tp2_team_2_teamnumber.py Enter the name of the dataset folder: ML_Images Enter the name of the metadata file: img_metadata.csv Enter the name of output csv features file: img_features.csv

Training Metadata DataFrame: ClassId Path 0 0 0001.png 1 1 0002.png 2 0 0003.png 3 1 0004.png 4 0 0005.png

Features have been extracted and saved to img_features.csv

Feature Dataset Shape: (1080, 11)

Feature Dataset Head: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 0 153.2073 61.059767 91.9556 40.786746 17.8427 19.008287 2.0 0.0 0.0 0001.png 0 1 81.7387 87.964121 151.8222 55.362343 126.3639 91.494998 49.0 1.0 0.0 0002.png 1 2 153.4376 57.676740 95.8184 38.068961 21.2990 22.463993 1.0 0.0 0.0 0003.png 0 3 82.5612 83.471986 147.9150 63.461210 127.8036 90.167103 42.0 1.0 0.0 0004.png 1 4 154.4448 57.903069 95.9104 40.238663 24.2915 24.985162 1.0 0.0 0.0 0005.png 0

Enter the Path of the image you want to display the features for: 0076.png

Features for 0076.png: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 75 89.5298 81.860591 174.9489 49.705431 142.4668 73.09418 46.0 1.0 0.0 0076.png 1

Case 2 Sample Output

$ python3 tp2_team_2_teamnumber.py Enter the name of the dataset folder: ML_Images Enter the name of the metadata file: img_metadata.csv Enter the name of output csv features file: img_features.csv

Training Metadata DataFrame: ClassId Path 0 0 0001.png 1 1 0002.png 2 0 0003.png 3 1 0004.png 4 0 0005.png

Features have been extracted and saved to img_features.csv

Feature Dataset Shape: (1080, 11)

Feature Dataset Head: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 0 153.2073 61.059767 91.9556 40.786746 17.8427 19.008287 2.0 0.0 0.0 0001.png 0 1 81.7387 87.964121 151.8222 55.362343 126.3639 91.494998 49.0 1.0 0.0 0002.png 1 2 153.4376 57.676740 95.8184 38.068961 21.2990 22.463993 1.0 0.0 0.0 0003.png 0 3 82.5612 83.471986 147.9150 63.461210 127.8036 90.167103 42.0 1.0 0.0 0004.png 1 4 154.4448 57.903069 95.9104 40.238663 24.2915 24.985162 1.0 0.0 0.0 0005.png 0

Enter the Path of the image you want to display the features for: 0999.png

Features for 0999.png: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 998 75.638 90.991287 70.6593 77.218499 3.2547 3.841279 0.0 0.0 0.0 0999.png 0

Case 3 Sample Output

$ python3 tp2_team_2_teamnumber.py Enter the name of the dataset folder: ML_Images Enter the name of the metadata file: img_metadata.csv Enter the name of output csv features file: img_features.csv

Training Metadata DataFrame: ClassId Path 0 0 0001.png 1 1 0002.png 2 0 0003.png 3 1 0004.png 4 0 0005.png

Features have been extracted and saved to img_features.csv

Feature Dataset Shape: (1080, 11)

Feature Dataset Head: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 0 153.2073 61.059767 91.9556 40.786746 17.8427 19.008287 2.0 0.0 0.0 0001.png 0 1 81.7387 87.964121 151.8222 55.362343 126.3639 91.494998 49.0 1.0 0.0 0002.png 1 2 153.4376 57.676740 95.8184 38.068961 21.2990 22.463993 1.0 0.0 0.0 0003.png 0 3 82.5612 83.471986 147.9150 63.461210 127.8036 90.167103 42.0 1.0 0.0 0004.png 1 4 154.4448 57.903069 95.9104 40.238663 24.2915 24.985162 1.0 0.0 0.0 0005.png 0

Enter the Path of the image you want to display the features for: 0176.png

Features for 0176.png: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 175 118.1767 67.818076 127.7954 69.425612 52.293 46.219435 25.0 0.0 0.0 0176.png 1

Case 4 Sample Output

$ python3 tp2_team_2_teamnumber.py Enter the name of the dataset folder: ML_Images Enter the name of the metadata file: img_metadata.csv Enter the name of output csv features file: img_features.csv

Training Metadata DataFrame: ClassId Path 0 0 0001.png 1 1 0002.png 2 0 0003.png 3 1 0004.png 4 0 0005.png

Features have been extracted and saved to img_features.csv

Feature Dataset Shape: (1080, 11)

Feature Dataset Head: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 0 153.2073 61.059767 91.9556 40.786746 17.8427 19.008287 2.0 0.0 0.0 0001.png 0 1 81.7387 87.964121 151.8222 55.362343 126.3639 91.494998 49.0 1.0 0.0 0002.png 1 2 153.4376 57.676740 95.8184 38.068961 21.2990 22.463993 1.0 0.0 0.0 0003.png 0 3 82.5612 83.471986 147.9150 63.461210 127.8036 90.167103 42.0 1.0 0.0 0004.png 1 4 154.4448 57.903069 95.9104 40.238663 24.2915 24.985162 1.0 0.0 0.0 0005.png 0

Enter the Path of the image you want to display the features for: 0761.png

Features for 0761.png: hue_mean hue_std saturation_mean saturation_std value_mean value_std number_lines has_circle has_triangle Path ClassId 760 27.37 53.153744 112.256 73.724648 37.1103 40.384699 28.0 0.0 1.0 0761.png 0

Table 6.8 Deliverables#

Deliverables

Description

tp2_team_2_teamnumber.pdf

Flowchart(s) for this task.

tp2_team_2_teamnumber.py

Your completed Python code.