Saturday, August 2, 2025

When Identity Security Becomes a Wall — Not a Shield

After a breach that forced a reset of my digital identity, I hit a roadblock I never anticipated: multi-factor authentication (2FA) locked me out of critical Microsoft services with no reliable way to prove who I was.

Despite years of interaction, billing history, and documented correspondence, access couldn’t be restored. Support channels were opaque. Recovery methods? Virtually nonexistent.

🧩 The Fallout

This isn’t just a tale of frustration — it’s a wake-up call for anyone who depends on digital platforms for professional continuity. Here's what made this situation particularly troubling:

  • 2FA mechanisms ignored reset conditions and created a closed loop
  • Microsoft’s support structure lacked escalation flexibility for identity restoration
  • Existing billing relationships didn’t help validate re-entry
  • Submission of supporting materials was not possible due to access barriers
  • Communication was throttled by the very safeguards meant to protect users

📁 Appendix Overview (Bullet Format)

Though I’ve withheld raw screenshots for privacy, the underlying evidence includes:

  • Email chains across multiple support tiers
  • Billing confirmation across service subscriptions
  • Failed attempts to upload documents for verification
  • Timeline logs of authentication attempts
  • Chat transcripts documenting escalation effort
  • Account alerts post-identity reset
  • Case numbers and references from support tools
  • License access history and dashboard exclusions
  • Anomalies in MFA re-enrollment
  • Failed access attempts after password and device reset
  • Time-based snapshot of support delays and breakdowns

🔄 What’s Next?

This blog isn’t about placing blame — it’s about demanding resilience. If identity protection policies don’t account for edge-case scenarios, platform continuity suffers.

Lesson learned: Security tools should protect users with them, not from them.



Monday, July 28, 2025

Is Your Computer Running Out of Space? Reclaim It Before You Buy!

Sooner or later, most of us find ourselves in a frustrating situation: our computer is running low on storage space. This can severely restrict what you can do, making you feel like you desperately need a new computer. While a new computer can be exciting, it also means a significant cost and the tedious task of transferring all your important files and folders – a task that often gets delayed indefinitely!

But before you commit to a new computer or subscribe to expensive cloud storage, let's take a hard look at what's eating up your current space. Is it essential? How can you get that space back? Let's find out!

Step 1: Discover What's Taking Up Space

The first thing to do is find out how much storage you're currently using.

Quick Check:

  1. Open File Explorer.

  2. Right-click on your C: drive (usually labeled "Local Disk (C:)").

  3. Select "Properties." You'll see a pie chart showing how much space is used and how much is free.

Detailed Look (Windows 11): For a more detailed breakdown, Windows 11 offers a great visual tool.

  1. In the Windows Search bar, type "System" and select "System" from the results.

  2. Navigate to "Storage" (or "Storage sense").

This view breaks down your storage by categories like "Installed apps," "Temporary files," and "Photos." Here's an example of what your storage might look like before you start cleaning:

You might notice that certain categories, like "Temporary files," take up a surprising amount of space!

Step 2: Clean Up "Temporary Files" – Your Easiest Win!

"Temporary files" are often the biggest culprit and the safest to remove without any penalty. These are files created by Windows and other programs that are no longer needed. They include things like temporary internet files, downloaded program files, and temporary files created by applications. They accumulate over time and can be safely deleted.

How to Delete Temporary Files:

  • Using Command Prompt (Quick Method):

    1. Open Windows Search, type "cmd", and select "Command Prompt."

    2. Type del %temp% and press Enter. This command deletes files in your user's temporary folder.

  • Using Built-in Tools:

    • Disk Cleanup: Windows has a built-in tool called "Disk Cleanup." You can find it by searching for it in the Windows search bar. It allows you to select various categories of files to delete, including temporary files, Recycle Bin contents, and more.

    • Third-party Tools: Tools like CCleaner also exist, but for most users, Windows' built-in options are sufficient and safe.

Step 3: Deeper Cleaning with DISM

For an even more thorough cleanup, especially to recover space from the Windows Component Store (often called the "WinSxS folder"), the Deployment Image Servicing and Management (DISM) tool is incredibly powerful. While DISM has many advanced functions (you might see a lot of commands if you just type dism in Command Prompt), we'll use a specific command for cleaning.

How to Use DISM for Cleanup:

  1. Open Command Prompt as an administrator. To do this, search for "cmd", right-click on "Command Prompt," and select "Run as administrator."

  2. In the Command Prompt window, type (or copy and paste) the following command and press Enter:

    Dism.exe /Online /Cleanup-Image /StartComponentCleanup
    

    You will see output similar to this as it runs:

    Deployment Image Servicing and Management tool
    Version: 10.0.26100.1150
    
    Image Version: 10.0.26100.4770
    
    [==========================100.0%==========================]
    The operation completed successfully.
    

    The [==========================100.0%==========================] indicates the process is complete.

Step 4: Check Your Regained Space!

After running the DISM cleanup, it's time to see the results! Go back to your Windows System Storage settings (as in Step 1). You should notice a significant difference in the amount of free space.

Here's an example of my storage after a cleaning operation, showing the considerable space recovered:

As you can observe, I recovered considerable storage space. By taking these simple steps, you can significantly extend the life and usability of your current computer, saving you money and the hassle of a new system!

DISM is a very powerful built-in tool and I recommend using it. Invoking and inspecting the various uses of DISM can be see by invoking it from caommand line as follows;


C:\Windows\System32>dism

Deployment Image Servicing and Management tool

Version: 10.0.26100.1150

DISM.exe [dism_options] {Imaging_command} [<Imaging_arguments>]

DISM.exe {/Image:<path_to_offline_image> | /Online} [dism_options]

         {servicing_command} [<servicing_arguments>]


DESCRIPTION:


  DISM enumerates, installs, uninstalls, configures, and updates features

  and packages in Windows images. The commands that are available depend

  on the image being serviced and whether the image is offline or running.



FFU COMMANDS:


  /Capture-Ffu            - Captures a physical disk image into a new FFU file.

  /Apply-Ffu              - Applies an .ffu image.

  /Split-Ffu              - Splits an existing .ffu file into multiple read-only

                            split FFU files.

  /Optimize-Ffu           - Optimizes a FFU file so that it can be applied to storage

                            of a different size.


WIM COMMANDS:


  /Apply-CustomDataImage  - Dehydrates files contained in the custom data image.

  /Capture-CustomImage    - Captures customizations into a delta WIM file on a

                            WIMBoot system. Captured directories include all

                            subfolders and data.

  /Get-WIMBootEntry       - Displays WIMBoot configuration entries for the

                            specified disk volume.

  /Update-WIMBootEntry    - Updates WIMBoot configuration entry for the

                            specified disk volume.

  /List-Image             - Displays a list of the files and folders in a

                            specified image.

  /Delete-Image           - Deletes the specified volume image from a WIM file

                            that has multiple volume images.

  /Export-Image           - Exports a copy of the specified image to another

                            file.

  /Append-Image           - Adds another image to a WIM file.

  /Capture-Image          - Captures an image of a drive into a new WIM file.

                            Captured directories include all subfolders and

                            data.

  /Get-MountedWimInfo     - Displays information about mounted WIM images.

  /Get-WimInfo            - Displays information about images in a WIM file.

  /Commit-Wim             - Saves changes to a mounted WIM image.

  /Unmount-Wim            - Unmounts a mounted WIM image.

  /Mount-Wim              - Mounts an image from a WIM file.

  /Remount-Wim            - Recovers an orphaned WIM mount directory.

  /Cleanup-Wim            - Deletes resources associated with mounted WIM

                            images that are corrupted.


GENERIC IMAGING COMMANDS:


  /Split-Image            - Splits an existing .wim file into multiple

                            read-only split WIM (SWM) files.

  /Apply-Image            - Applies an image.

  /Get-MountedImageInfo   - Displays information about mounted WIM and VHD

                            images.

  /Get-ImageInfo          - Displays information about images in a WIM, a VHD

                            or a FFU file.

  /Commit-Image           - Saves changes to a mounted WIM or VHD image.

  /Unmount-Image          - Unmounts a mounted WIM or VHD image.

  /Mount-Image            - Mounts an image from a WIM or VHD file.

  /Remount-Image          - Recovers an orphaned image mount directory.

  /Cleanup-Mountpoints    - Deletes resources associated with corrupted

                            mounted images.


IMAGE SPECIFICATIONS:


  /Online                 - Targets the running operating system.

  /Image                  - Specifies the path to the root directory of an

                            offline Windows image.


DISM OPTIONS:


  /English                - Displays command line output in English.

  /Format                 - Specifies the report output format.

  /WinDir                 - Specifies the path to the Windows directory.

  /SysDriveDir            - Specifies the path to the system-loader file named

                            BootMgr.

  /LogPath                - Specifies the logfile path.

  /LogLevel               - Specifies the output level shown in the log (1-4).

  /NoRestart              - Suppresses automatic reboots and reboot prompts.

  /Quiet                  - Suppresses all output except for error messages.

  /ScratchDir             - Specifies the path to a scratch directory.


For more information about these DISM options and their arguments, specify an

option immediately before /?.


  Examples:

    DISM.exe /Mount-Wim /?

    DISM.exe /ScratchDir /?

    DISM.exe /Image:C:\test\offline /?

    DISM.exe /Online /?

As far as cleaning is concerned, it may be evoked from the command line as follows:

C:\Windows\System32>Dism.exe /Online /Cleanup-Image /StartComponentCleanup

Deployment Image Servicing and Management tool
Version: 10.0.26100.1150

Image Version: 10.0.26100.4770

[==========================100.0%==========================]
The operation completed successfully.


Here is a widely read post on DISM from 11 years ago on this blog:


 

Saturday, July 5, 2025

How do you visualize glucose data from a glucose CGM report using Python?

Interpretation of data from the python code from my previous post must be exercised with caution. The program explicitly puts red dots over the curve to establish verification that the method indeed achieves its objective. We have seen that the image generated does show large red circles riding the graph. 

It is easy to show by reducing the size to 0, that the curve is traced by very tiny circles shown by an underlying red ness in the trace (zoom to see). These are shown for two the two radii assumed in the next figure.

 


However, a .CSV file is generated by the code. The graphing of this file in Microsoft Excel does not show any difference between the two cases, radius=2 and radius =0 as shown.

  


The reason is these curves (one by visualization and the other set by generated CSV file) are not using the same base.

1. The cv2.circle() visualization: This is what creates the glucose_trace_detected.jpg. 

2. The matplotlib.pyplot.plot() or scatter() visualization: This is what creates the glucose_value_mg_dL plo.

However, in the plotted graphs from CSV files we find gaps in the generated graph. They can indeed correspond to event marker positions. The red dot in the visualization is only for verifying that the entire graph is captured.

We will take a look at this possibility of getting the psotional data of events in the next post, as it gives us valuable information on event times. 


Friday, July 4, 2025

How do you extract glucose data from a glucose CGM report using Python?

 In my previous post, I described how to find the graph of glucose vs. time using a blue mask (the curve was in blue). This post is about graphing Glucose data vs. time using the curve from the previous post.

The main result of the previous post resulted in developing a mask’ for the curve as shown here.

This Python script (GlucoseCalibrated.py) that follows is designed to extract numerical glucose data from a graph image by analyzing the blue glucose trace, calibrating it to real-world glucose values, and then saving the results. The important determinations that are to be made are, 1) Correctly rendering the Y-axis of the displayed curve and establishing the Glucose data in mg/dL correctly to the numerical data from the calculation. In associating the correct Glucose values, the measurements on the curve from the report were made using GIMP. The X-axis association with time has to be done as well.

import cv2
import numpy as np
import matplotlib.pyplot as plt
import csv
import os

# --- Configuration ---

IMAGE_PATH = "LibreViewOneDayGraph.jpg"

LOWER_BLUE = np.array([100, 50, 50])
UPPER_BLUE = np.array([140, 255, 255])

# --- CRITICAL CALIBRATION PARAMETERS ---
GLUCOSE_VALUE_1 = 0
PIXEL_Y_1 = 170

GLUCOSE_VALUE_2 = 150
PIXEL_Y_2 = 107
# ----------------------------------------

print(f"Attempting to load image from: {IMAGE_PATH}. "
f"Current working directory: {os.getcwd()}")

# Load image
img = cv2.imread(IMAGE_PATH)

# Check if the image was loaded successfully
if img is None:
print(f"Error: Could not load image from {IMAGE_PATH}. "
"Please check the path and name.")
else:
print(f"Image '{IMAGE_PATH}' loaded successfully. "
f"Dimensions: {img.shape}")
img_height, img_width, _ = img.shape
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Create mask for the blue color
mask = cv2.inRange(img_hsv, LOWER_BLUE, UPPER_BLUE)
print(f"Mask created. Number of blue pixels detected: "
f"{np.count_nonzero(mask)}")
if np.count_nonzero(mask) == 0:
print("Warning: No blue pixels found in the image with the "
"current HSV range. Check image color or HSV values.")

# Create a copy of the image to draw on (for visualization)
img_with_circles = img.copy()
circle_radius = 2

# List to store x, y coordinates and glucose values for CSV
glucose_trace_data = []
glucose_trace_data.append(
['x_pixel', 'y_pixel_raw', 'glucose_value_mg_dL'])

# --- Calculate the linear transformation parameters (m and b) ---
m_glucose_scale = (GLUCOSE_VALUE_2 - GLUCOSE_VALUE_1) / \
(PIXEL_Y_2 - PIXEL_Y_1)
b_glucose_offset = GLUCOSE_VALUE_1 - \
(m_glucose_scale * PIXEL_Y_1)

print(f"\n--- Calibration Results ---")
print(f"Using calibration points: ({PIXEL_Y_1}px -> "
f"{GLUCOSE_VALUE_1}mg/dL) and ({PIXEL_Y_2}px -> "
f"{GLUCOSE_VALUE_2}mg/dL)")
print(f"Calculated Glucose Scale (m): {m_glucose_scale:.4f}")
print(f"Calculated Glucose Offset (b): {b_glucose_offset:.4f}")
print(f"---------------------------\n")

# Iterate through each x-position to find the glucose trace
print("Starting trace detection loop...")
for x_pos in range(img_width):
column = mask[:, x_pos]
y_hits = np.where(column > 0)[0]

if len(y_hits) > 0:
y_pixel_raw = int(np.median(y_hits))

actual_glucose_value = \
(m_glucose_scale * y_pixel_raw) + b_glucose_offset
actual_glucose_value_rounded = round(actual_glucose_value, 2)

cv2.circle(img_with_circles, (x_pos, y_pixel_raw),
circle_radius, (0, 0, 255), -1)

glucose_trace_data.append(
[x_pos, y_pixel_raw, actual_glucose_value_rounded])
print(f"Trace detection loop finished. Data points collected: "
f"{len(glucose_trace_data) - 1}")

# --- Display the visualized image ---
plt.figure(figsize=(12, 7))
plt.imshow(cv2.cvtColor(img_with_circles, cv2.COLOR_BGR2RGB))
plt.title("Detected Glucose Trace on Original Image "
"(with Calibration Notes)")
plt.axis("off")
plt.show()

# Save the image with circles
output_image_filename = "glucose_trace_detected.jpg"
cv2.imwrite(output_image_filename, img_with_circles)
print(f"Visualized trace image saved to {output_image_filename}")

# --- Save data to CSV file ---
csv_filename = "glucose_trace_data_calibrated_values.csv"
try:
with open(csv_filename, 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerows(glucose_trace_data)
print(f"Glucose trace data saved to {csv_filename}")
except IOError:
print(f"Error: Could not write to {csv_filename}. "
"Please check file permissions or if the file is open.")

print("\nScript finished execution.")

Here's a step-by-step summary of its functionality:

  1. Import Libraries: It imports cv2 (OpenCV) for image processing, numpy for numerical operations, matplotlib.pyplot for displaying images, csv for writing data to CSV files, and os for path-related diagnostics.
  2. Configuration:
    • IMAGE_PATH: Specifies the path to your input glucose graph image (e.g., "LibreViewOneDayGraph.jpg"). This is the image the script will analyze.
    • LOWER_BLUE, UPPER_BLUE: These define a range in the HSV color space (Hue, Saturation, Value) that precisely represents the "blue" color of your glucose trace. Any pixels falling within this range are considered part of the trace.
  3. Critical Calibration Parameters:
    • GLUCOSE_VALUE_1, PIXEL_Y_1: These are your first calibration point. GLUCOSE_VALUE_1 is a known glucose level (e.g., 0 mg/dL), and PIXEL_Y_1 is the exact Y-pixel coordinate you manually measured from the image corresponding to that glucose level.
    • GLUCOSE_VALUE_2, PIXEL_Y_2: These are your second calibration point, representing another known glucose level (e.g., 150 mg/dL) and its corresponding Y-pixel coordinate.
    • Purpose: These two points are crucial for establishing a linear relationship between pixel Y-coordinates and actual glucose values.
  4. Image Loading and Initial Checks:
    • The script attempts to load the image specified by IMAGE_PATH.
    • It checks if the image loaded successfully. If not, it prints an error and stops.
    • It converts the image from BGR (Blue, Green, Red - OpenCV's default) to HSV (Hue, Saturation, Value) color space, as HSV is generally better for color-based segmentation.
  5. Color Masking:
    • cv2.inRange(img_hsv, LOWER_BLUE, UPPER_BLUE) creates a binary mask. This mask is a black-and-white image where white pixels represent areas of the original image that fall within the defined blue color range (i.e., your glucose trace).
    • It prints the number of detected blue pixels as a diagnostic.
  6. Calibration Calculation:
    • It calculates the m (slope) and b (y-intercept) values for a linear equation: Glucose_Value = m * raw_y_pixel + b.
    • m_glucose_scale: Represents how many mg/dL each pixel unit corresponds to. It's calculated using the two calibration points. It's typically negative because a higher pixel Y-value (further down the image) means a lower glucose value on the graph.
    • b_glucose_offset: The offset, calculated using one of the calibration points and the derived slope.
    • These values form the core of converting pixel data to meaningful glucose readings.
  7. Glucose Trace Detection Loop:
    • The script iterates through every single vertical column (x_pos) of the image, from left to right.
    • For each column, it looks at the mask to find all y_hits (white pixels, indicating blue color from the trace).
    • np.median(y_hits): If blue pixels are found in a column, it takes the median Y-coordinate of those pixels. Using the median helps to make the detection robust against noise or slight variations in line thickness.
    • Calibration Application: The y_pixel_raw (median Y-coordinate) is then fed into the linear equation (m * y_pixel_raw + b) to get the actual_glucose_value_rounded.
    • Visualization: A red circle is drawn on a copy of the original image (img_with_circles) at the (x_pos, y_pixel_raw) to visually confirm where the trace was detected.
    • Data Storage: The x_pos, y_pixel_raw, and actual_glucose_value_rounded are stored in the glucose_trace_data list.
  8. Output and Saving:
    • Display Image: The img_with_circles (original graph with red dots on the detected trace) is displayed using matplotlib.
    • Save Image: This visualized image is also saved as glucose_trace_detected.jpg.
    • Save CSV: The glucose_trace_data (containing all the detected x, y, and calibrated glucose values) is saved to a CSV file named glucose_trace_data_calibrated_values.csv.

In essence, this script automates the process of "reading" your glucose graph by color detection and translating the visual curve into a precise set of numerical data points.

Here is the result of running this code:

Attempting to load image from: LibreViewOneDayGraph.jpg. Current working directory: C:\Users\hoden\PycharmProjects\GraphtoData

Image 'LibreViewOneDayGraph.jpg' loaded successfully. Dimensions: (244, 1140, 3)

Mask created. Number of blue pixels detected: 5325


--- Calibration Results ---

Using calibration points: (170px -> 0mg/dL) and (107px -> 150mg/dL)

Calculated Glucose Scale (m): -2.3810

Calculated Glucose Offset (b): 404.7619

---------------------------

Starting trace detection loop...

Trace detection loop finished. Data points collected: 1091

Visualized trace image saved to glucose_trace_detected.jpg

Glucose trace data saved to glucose_trace_data_calibrated_values.csv

Script finished execution.

Process finished with exit code 0

The CSV file is written starting at the beginning of the curve to the end and at each point a red dot is placed on the original to unambiguosly verify that the whole curve is covered. 


Saturday, June 28, 2025

Can Your Code Detect Colors in Images? Image Processing with OpenCV & Python

 

Can Your Code Detect Colors in Images? Isolating Features with a Mask (Part 1)


Introduction

Ever wondered how software can 'see' and interpret specific lines or shapes in an image? In this series, we'll dive into the basics of image processing with Python and OpenCV, focusing on how to detect features based on their color. We'll start with a common and fundamental technique: creating a color mask.

We'll use a real-world example: extracting the glucose curve from a FreeStyle Continuous Glucose Monitor (CGM) sensor graph. This program (MaskImage.py) will show you how to detect a specific blue-colored feature in an image.

Here's the kind of graph we're working with:


Here is an image stripped of text, which we will use in our code example:


Note: The image used in the code where the blue rectangle has been erased.

(Note: In an earlier post(https://hodentekhelp.blogspot.com/2025/06/can-python-really-read-text-from-any.html),we covered extracting text from sensor reports. You can find that tutorial.


The Core Idea: Isolating Colors

When an image processing program "looks" at an image, it sees a grid of pixels, each with a numerical color value. To find a specific object or line, one powerful method is to isolate all pixels that fall within a certain color range. This is where a "mask" comes in.

Understanding Color Spaces: Why HSV?

First, we load our image. Notice we convert it from BGR (Blue, Green, Red – the default way OpenCV reads colors) to HSV (Hue, Saturation, Value). Why HSV? It's super useful for color detection because it separates the actual color (Hue) from how vibrant (Saturation) or bright/dark (Value) it is. This makes our color detection more reliable, even if the lighting in your image isn't perfect.

For our blue glucose curve, we define a range of HSV values that represent "blue."

Creating the Color Mask

The magic happens with cv2.inRange(). This function scans every pixel in our HSV image. If a pixel's color falls within our defined blue range, it turns that pixel white (255) in a new, binary image called the mask. Otherwise, it turns the pixel black (0). Think of the mask as a filter: it isolates only the blue parts of our graph, making them stand out against a black background.


Let's See the Code: MaskImage.py (Part 1)

import cv2  # OpenCV library for image processing
import numpy as np  # NumPy for numerical operations, especially with arrays
import matplotlib.pyplot as plt  # Matplotlib for displaying images and plots

# 1. Load the image and convert its color space
# Make sure "LibreViewOneDayGraph.jpg" is in the same directory as your Python script.
img = cv2.imread("LibreViewOneDayGraph.jpg")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 2. Define the color range for our blue glucose curve
# [Hue, Saturation, Value] - These values define the specific range for "blue".
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([140, 255, 255])

# 3. Create the 'mask' - our color filter!
# This function identifies all pixels within the specified blue HSV range.
mask = cv2.inRange(img_hsv, lower_blue, upper_blue)

# --- Visualize the Mask ---
# We'll display the original image and the generated mask side-by-side.
plt.figure(figsize=(12, 6)) # Adjust figure size for better viewing

plt.subplot(1, 2, 1) # This sets up a plot grid: 1 row, 2 columns, first plot
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # Convert back to RGB for Matplotlib display
plt.title("Original Image")
plt.axis("off") # Hide axes for cleaner image presentation

plt.subplot(1, 2, 2) # Second plot in our grid
plt.imshow(mask, cmap='gray') # Display the 'mask' in grayscale (black and white)
plt.title("Generated Mask (Blue Trace Highlighted)")
plt.axis("off") # Hide axes

plt.show() # Show both plots

Key Code Lines Explained

Let's break down the most important parts of the code:

  • import cv2, import numpy as np, import matplotlib.pyplot as plt: These lines bring in the necessary libraries. cv2 (OpenCV) is for image processing, numpy for handling numerical arrays (images are treated as arrays of pixels), and matplotlib.pyplot for displaying images.

  • img = cv2.imread("LibreViewOneDayGraph.jpg"): This line reads your image file from the specified path. Make sure the image file is in the same directory as your Python script, or provide the full path to the image.

  • img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV): This is crucial. It converts your image's color representation from BGR (Blue-Green-Red, which is how OpenCV loads images by default) to HSV (Hue-Saturation-Value). HSV makes it much easier to isolate colors based on their "true" shade, rather than a mix of red, green, and blue components.

  • lower_blue = np.array([100, 50, 50]) and upper_blue = np.array([140, 255, 255]): These NumPy arrays define the range of HSV values that we consider "blue."

    • The first number (0-179) is Hue, representing the actual color (e.g., 0 is red, 60 is yellow, 120 is blue).

    • The second number (0-255) is Saturation, representing the intensity or "purity" of the color (0 is gray, 255 is vibrant).

    • The third number (0-255) is Value, representing the brightness (0 is black, 255 is bright). We've set a range that should capture most shades of blue without picking up other colors.

  • mask = cv2.inRange(img_hsv, lower_blue, upper_blue): This is the core filtering step. It takes your HSV image and applies the defined color range. Any pixel whose HSV value falls within lower_blue and upper_blue will be set to white (255) in the mask image, and all other pixels will be set to black (0).

  • plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) and plt.imshow(mask, cmap='gray'): These lines use Matplotlib to display the images.

    • For the original image, we convert it back to RGB (cv2.COLOR_BGR2RGB) because Matplotlib expects images in RGB format, unlike OpenCV's BGR.

    • For the mask, we tell Matplotlib to display it as a grayscale image (cmap='gray') since it's a binary (black and white) image.


What Just Happened?

The images created by the subplots are shown here:

In the left subplot, you saw the original glucose graph. On the right, you saw the mask image. Notice how the mask is mostly black, but the blue glucose curve from the original image now appears as a distinct white curve. This mask is a powerful tool because it allows us to easily focus on and extract the specific feature we're interested in, ignoring all the surrounding details like grid lines or text.

Next Steps & Beyond Color Detection

We've now successfully isolated our target feature using its color! In the next part of this series, we'll explore how to take this mask and actually extract the precise pixel coordinates of the curve. In the next part, we'll walk along the x-axis and pick up y-intersections, creating (X,Y) coordinate pairs that can then be saved to a CSV file for further analysis.

While this method works great for distinct colors like blue, detecting features that are black, white, or various shades of gray often requires different techniques (like grayscale thresholding or edge detection). We'll briefly touch upon these advanced concepts in future discussions, but for now, you've mastered the art of color-based masking!

Getting Started:

If you need a refresher on how to import and set up Python libraries, please refer to our earlier tutorials on this blog.