1. Introduction

In this project, we analyze the color measurements of prints of Douglas color cards. The primary goal is to assess the consistency of color prints relative to the intended color values and explore color dispersion. The MasterColorCard file consists of Field (No., 1 to 64), for a single color card and Crow, Ccol (1 to 8 each, for the position on a single color card).

The LabMeasurements-Color-Card consists of color measurements for 42 color cards printed on one large sheet with 7 rows and 6 columns. each color card has 64 color spots. The data source has thirteen of the large sheets which is 42*13 color cards.

We will visualize the data to understand:

  • How do colors behave relative to the print master?
  • Color dispersion on individual Lab scales
  • Differential behavior between targets and color spots
  • Quality concerning the intended color

The project involves the following steps:

  • Reading and preprocessing the data
  • Analyzing color dispersion and consistency across sheets
  • Visualizing intended colors and measured colors
  • Calculating color differences (ΔE values)
  • Variance Calculation
  • Closest Cards to Mean Visualization
  • Analyzing Variance Across Sheets

2. Description of Data

In this report, we utilize two distinct datasets to conduct our analysis:

  • MasterColorCard
  • LabMeasurements-Color-Card

2.1 MasterColorCard Conversion and Visualization

We explored the MasterColorCard, a collection of colors, to understand its wide range of colors. The MasterColorCard uses the L*a*b color space, which is great for describing how similar colors look to the human eye. However, to view these colors on digital screens, we need to convert them to the RGB color space, which is the standard format for screens. By converting from L*a*b to RGB, we were able to accurately display the colors in our dataset for visualization. After converting the colors, we visualized them using a scatter plot, where each color is represented by a square or a circle.

p2

2.2 Color Dispersion Across Sheets

This visualization aims to analyze and compare the color consistency across a dataset comprising 13 sheets, each containing 64 spots of color cards. The ΔE values for these spots have been computed to represent the color differences in the Lab color space. A lower ΔE value signifies higher color consistency, while a higher ΔE value indicates significant color variation. The 'inferno' colormap is used to highlight variations in color differences. The color intensity in the plot reflects the magnitude of these differences, with brighter spots indicating higher ΔE values and greater color deviation.

colrdispersion

3. Data Preprocessing

3.1 Data Loading and Initialization

  • Read and import the lab measurement data from the CSV file.
    df_lab = pd.read_csv("LabMeasurements-Color-Card.csv",sep=';', decimal=',') 
  • Initialize the dictionary to store data for each sheet.
    sheets = {}  

3.2 Sheet Data Extraction and Concatenation

  • Iterate through each sheet to extract relevant data based on rows and columns.
  • Concatenate selected data into a comprehensive DataFrame for each sheet.
    new_df = pd.concat(sheet_1, axis=1).T.reset_index(drop=True)

3.3 Reshape Data for Analysis

  • Extracting and reshaping to prepare for structured storage.
    
                                        for index, row in test_df.iterrows():
                                            # Iterate over each 8x8 block in the row
                                            for i in range(1, 9):
                                                for j in range(1, 9):
                                                    # Extract the L, a, b values for the block
                                                    L = row[f'L{i}{j}']
                                                    a = row[f'a{i}{j}']
                                                    b = row[f'b{i}{j}']
    
                                                    # Append the values to the reshaped data
                                                    reshaped_data.append({
                                                        'row': initial_row,
                                                        'column': initial_column,
                                                        'L': L,
                                                        'a': a,
                                                        'b': b
                                                    })
  • Creating reshaped_df to organize color card data into a structured format.
  • 
                                        reshaped_df = reshaped_df.astype({
                                            'row': int,
                                            'column': int,
                                            'L': float,
                                            'a': float,
                                            'b': float
                                        })
                    

3.4 Create Nested Dictionaries

  • In this step we have created the dictionary of dictionaries where each key of the parent dictionary is the sheet number, and the value is the dictionary of that particular sheet , for the child dictionary, key is the 'card'number, value is the 'L,a,b' dataframe.
  • Next we have populated dictionaries within each sheet to store color card data.
  • sheets[f'{no_sheets}'] = list_of_cards

3.5 Data_Processing_Workflow

4. Visualization of ΔE Values

This section explains how to calculate the color difference (ΔE) between two 8x8 color cards using the given Python code and Visualise afterwards.


4.1 Difference Between Two Cards

4.1.1 Understanding L, a, b

  • L, a, b Color Space:
    • The L, a, b color space, also known as CIELAB, is a color space defined by the International Commission on Illumination (CIE).
    • L: Lightness, ranging from 0 (black) to 100 (white).
    • a: Color component from green to red, where negative values indicate green and positive values indicate red.
    • b: Color component from blue to yellow, where negative values indicate blue and positive values indicate yellow.

4.1.2. Calculation of Color Difference (ΔE)

  • Purpose:
    • ΔE quantifies the difference between two colors. It helps in comparing how similar or different two colors are perceived.

  • Formula:
    • The formula for calculating ΔE in the CIELAB color space is:
    • ΔE = sqrt((L_after - L_before)^2 + (a_after - a_before)^2 + (b_after - b_before)^2)

    • This formula calculates the Euclidean distance between two points in the L, a, b color space.

4.1.3. Code Explanation

  • Libraries Imported:
    • numpy: Used for numerical operations.
    • matplotlib.pyplot: Used for plotting the heatmap.
    • seaborn: Used for enhancing the heatmap visualization.

  • Function Definition:
    • calculate_delta_e(L_before, a_before, b_before, L_after, a_after, b_after): This function computes the ΔE value using the provided formula.

  • Data Preparation:
    • Assume color_card1 and color_card2 are DataFrames representing two color cards, each containing 64 rows (one for each color block) with columns 'L', 'a', 'b'.

  • Delta E Matrix Initialization:
    • delta_e_matrix = np.zeros((8, 8)): Initializes an 8x8 matrix to store ΔE values for each color block.

  • Extracting Color Blocks:
    • color_card1 = sheets['12']['0'] and color_card2 = sheets['12']['1']: Extracts the DataFrames for the two color cards to be compared.

4.1.4. Calculating ΔE for Each Block

  • Loop Through Blocks:
    • The nested for loops iterate over each block in the 8x8 grid:
    • for i in range(8):
                          for j in range(8):

    • Extract Color Values:
      • For each block (i, j), extract the L, a, b values from color_card1 and color_card2:
      • color_block1 = color_card1.iloc[8*i + j]
                            color_block2 = color_card2.iloc[8*i + j]
                            L_before, a_before, b_before = color_block1['L'], color_block1['a'], color_block1['b']
                            L_after, a_after, b_after = color_block2['L'], color_block2['a'], color_block2['b']

    • Calculate ΔE:
      • Use the calculate_delta_e function to compute the color difference for each block and store it in the delta_e_matrix:
      • delta_e_matrix[i, j] = calculate_delta_e(L_before, a_before, b_before, L_after, a_after, b_after)

4.1.5. Visualization

  • Heatmap:
    • sns.heatmap(delta_e_matrix, cmap='hot', annot=True): Plots a heatmap of the ΔE values, using a hot color map for visualization and annotating each cell with the ΔE value.
    • plt.title('Color Difference Between Two Color Cards'): Adds a title to the heatmap.
    • plt.show(): Displays the heatmap.

4.1.6. The Complete Code for visualisation

  • The provided code effectively calculates and visualizes the color difference (ΔE) between two 8x8 color cards by iterating through each color block, computing the ΔE for each pair of corresponding blocks, and displaying the results in a heatmap.

                    import numpy as np
                    import matplotlib.pyplot as plt
                    import seaborn as sns

                    def calculate_delta_e(L_before, a_before, b_before, L_after, a_after, b_after):
                    delta_e = np.sqrt((L_after - L_before)**2 + (a_after - a_before)**2 + (b_after - b_before)**2)
                    return delta_e

                    # Assuming color_card1 and color_card2 are your two color cards
                    # Each color card is a DataFrame with 64 rows and columns 'L', 'a', 'b'

                    delta_e_matrix = np.zeros((8, 8))

                    color_card1 = sheets['12']['0']
                    color_card2 = sheets['12']['1']

                    for i in range(8):
                    for j in range(8):
                        color_block1 = color_card1.iloc[8*i + j]
                        color_block2 = color_card2.iloc[8*i + j]
                        L_before, a_before, b_before = color_block1['L'], color_block1['a'], color_block1['b']
                        L_after, a_after, b_after = color_block2['L'], color_block2['a'], color_block2['b']
                        delta_e_matrix[i, j] = calculate_delta_e(L_before, a_before, b_before, L_after, a_after, b_after)

                    sns.heatmap(delta_e_matrix, cmap='hot', annot=True)
                    plt.title('Color Difference Between Two Color Cards')
                    plt.show()
                    

4.2 ΔE Heatmap

This plot shows us how closely the printed colors match the intended colors.Iterates through each sheet and card, compares the measured colors with the intended colors, and calculates the ΔE values. it can be plot for any sheets but The heatmap visualizes these ΔE values for Sheet 0, Card 0 . Each cell in the heatmap represents a spot on the color card, with the color indicating the magnitude of the ΔE value. The 'coolwarm' colormap is used, where cooler colors (blue) represent lower ΔE values (higher consistency) and warmer colors (red) signify higher ΔE values (greater variation).

Hotmap

4.3 Shared Error Contribution of all 13 Trials

In this step, we are trying to find the shared error contribution of all 13 trials.

4.3.1 The Intution Behind Shared Error :

The idea is to find the a specific card from each sheet, that has minimum color difference or minimum del E to the MasterCard among all 42 cards, out of all 13 sheets. So at the end of this step, we will have a list of 13 cards that have minimum color difference or minimum del E to the MsterCard, we may call these 13 cards as "Best Trial Card" for each sheet out of all 13 sheets. Next we should understand that those 13 cards will have little variation from the MasterCard, but how much do each so called "Best Cards" contribute to the total trial error ?, Let's try to find out step-by-step.

4.3.2 Identifying the Card with Minimum ΔE :

To find the card with the minimum ΔE in each sheet, we define a function that iterates through all cards in a sheet that calculates the average ΔE for each card when compared to the MasterCard, and identifies the card with the lowest average ΔE.


        # Find the card with the minimum delta E in each sheet
        def find_min_delta_e_card(sheet):
            min_delta_e = float('inf')
            min_card = None
            for card_id, card in sheet.items():
                delta_e = np.mean([color_difference(card.iloc[i], master_card.iloc[i]) for i in range(len(card))])
                if delta_e < min_delta_e:
                    min_delta_e = delta_e
                    min_card = card_id
            return min_card, min_delta_e

    

This function find_min_delta_e_card ensures we identify the card that best matches the MasterCard from each sheet.

4.3.3 Identifying Best Trial Cards :

We apply the find_min_delta_e_card function across all sheets to identify the "Best Trial Card" for each sheet and record the minimum ΔE values.


    min_delta_e_values = []
    for sheet_id, sheet in demo_sheets.items():
       min_card, min_delta_e = find_min_delta_e_card(sheet)
       min_delta_e_values.append((sheet_id, min_delta_e))

This process yields a list of tuples where each tuple contains a sheet ID and the corresponding minimum ΔE value.

4.3.4 Ranking Sheets by ΔE:

Next, we rank the sheets based on their minimum ΔE values. We also calculate the total ΔE and the percentage contribution of each sheet to this total. By sorting and calculating percentages, we can better understand which sheets have the closest match to the MasterCard and their relative contributions to the total error.


    # Rank the sheets by delta E values
    min_delta_e_values.sort(key=lambda x: x[1])
    total_delta_e = sum([x[1] for x in min_delta_e_values])
    percentages = [(x[0], (x[1] / total_delta_e) * 100) for x in min_delta_e_values]

4.3.5 Visualizing Error Contribution :

To visualize the error contribution of each sheet, we create a ring plot (donut chart) showing the percentage contribution of each sheet to the total trial error.


    import matplotlib.pyplot as plt

    # Plotting the ring plot
    fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(aspect="equal"))

    data = [x[1] for x in percentages]
    labels = [f'Sheet {x[0]}\n{round(x[1], 2)}%' for x in percentages]

    wedges, texts = ax.pie(data, wedgeprops=dict(width=0.3), startangle=-40)

    # Annotate with sheet numbers and percentages
    for i, p in enumerate(wedges):
        ang = (p.theta2 - p.theta1) / 2. + p.theta1
        y = np.sin(np.deg2rad(ang))
        x = np.cos(np.deg2rad(ang))

        horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]

        # Add straight line connection
        ax.annotate(labels[i], xy=(x, y), xytext=(1.35 * x, 1.4 * y),
                    horizontalalignment=horizontalalignment,
                    bbox=dict(boxstyle="round,pad=0.5", fc="w"),
                    arrowprops=dict(arrowstyle="-", connectionstyle="arc3,rad=0.0"))

    plt.suptitle("Error Contribution of All 13 Sheets", fontsize=20, y=1.02)
    plt.show()

4.3.6 Donut Chart Visualisation for Shared Error Contributio :

Hotmap

4.4 Acurracy of Trials across all the sheets

IN this step, we are trying to visualise how is the color difference between Mastercard and the 'Best Card' with "minimum del E" amongst each sheet across all 13 sheets.

If the plots are scrutinised closely then it can be easily intrepreted that for each sheet there is a strong difference in color between (6,2) squares of MasterCard and the "Best card" with minimum del E in that specific sheet amongst 42 cards.

4.4.1 Heatmap Visualisation : Accuracy of trials across all Sheets

The script visualizes these findings by plotting 13 heatmaps, illustrating the accuracy of trials across all sheets, with each heatmap representing a different sheet's data. These visualizations help in understanding how closely each trial card matches the master card, aiding in evaluating experimental outcomes efficiently.

Image

The Code Snippet


    # Plotting the 13 heatmaps
    fig, axs = plt.subplots(4, 4, figsize=(15, 15))
    axs = axs.flatten()

    for idx, card in enumerate(min_delta_e_cards):
        delta_e_matrix = calculate_delta_e_matrix(card, master_card)
        sns.heatmap(delta_e_matrix, cmap='Blues_r', cbar=False, ax=axs[idx])
        axs[idx].invert_yaxis()  # Invert the y-axis

        axs[idx].set_title(f'Sheet {idx + 1}')
        axs[idx].axis('off')

    # Remove extra subplots
    for idx in range(len(min_delta_e_cards), len(axs)):
        fig.delaxes(axs[idx])

    # Add a colorbar
    fig.subplots_adjust(bottom=0.1)
    cbar_ax = fig.add_axes([0.25, 0.05, 0.5, 0.02])
    fig.colorbar(axs[0].collections[0], cax=cbar_ax, orientation='horizontal', label='Delta E')

    plt.suptitle('Heatmap : Accuracy of trials across all Sheets', y=0.95, fontsize=25)
    plt.show()

4.4.2 Circular Color Difference with MasterCard : Accuracy of trials across Sheets

We visualize the color differences by plotting 13 subplots (arranged in a 3x5 grid) using Matplotlib. Each subplot represents a sheet from our dataset, where we identify and highlight the color card with the minimal delta E (color difference) compared to a predefined master card. Circles within each subplot depict the Lab color values of the ideal card (found to have minimal delta E) and the master card. The color of each square indicates the magnitude of color difference between the ideal card and the master card, ranging from black (no difference) to white (maximum difference). This visual approach helps in understanding how closely each experimental trial matches the master card, aiding in evaluating trial accuracy across all sheets effectively.

Image

The Code Snippet


    # Iterate through each sheet to plot minimum delta E cards
    plot_counter = 0
    for sheet_id, sheet in demo_sheets.items():
        if plot_counter >= 13:  # Adjust to the number of sheets you want to visualize
            break

        min_delta_e = float('inf')
        min_card = None

        # Find the card with minimum delta E in the current sheet
        for card_id, card in sheet.items():
            delta_e = np.mean([color_difference(card.iloc[i], master_card_df.iloc[i]) for i in range(len(card))])
            if delta_e < min_delta_e:
                min_delta_e = delta_e
                min_card = card_id

        # Plotting the minimum delta E card for the current sheet
        ideal_card = sheet[min_card]

        # Set axis limits
        axes[plot_counter].set_xlim(0, 8)
        axes[plot_counter].set_ylim(0, 8)

        # Invert y-axis to correct orientation
        #axes[plot_counter].invert_yaxis()

        for i in range(8):
            for j in range(8):
                color_diff = color_difference(ideal_card.iloc[i*8 + j], master_card_df.iloc[i*8 + j])
                intensity = color_diff / 100.0  # Normalize to range [0, 1]
                color = plt.cm.gray(1 - intensity)  # Black to white

                # Plotting the colored square
                rect = patches.Rectangle((j, i), 1, 1, color=color, alpha=1.0)
                axes[plot_counter].add_patch(rect)

                # Plotting the circles for ideal_card and master_card
                ideal_color = lab_to_rgb(ideal_card.iloc[i*8 + j]['L'], ideal_card.iloc[i*8 + j]['a'], ideal_card.iloc[i*8 + j]['b'])
                master_color = lab_to_rgb(master_card_df.iloc[i*8 + j]['L'], master_card_df.iloc[i*8 + j]['a'], master_card_df.iloc[i*8 + j]['b'])

                ideal_circle = patches.Circle((j + 0.35, i + 0.5), 0.3, edgecolor='none', facecolor=ideal_color)
                master_circle = patches.Circle((j + 0.65, i + 0.5), 0.3, edgecolor='none', facecolor=master_color)

                axes[plot_counter].add_patch(ideal_circle)
                axes[plot_counter].add_patch(master_circle)

        # Set title for each subplot
        axes[plot_counter].set_title(f'Sheet {sheet_id}')
        axes[plot_counter].set_xticks([])
        axes[plot_counter].set_yticks([])

        plot_counter += 1

    # Create a colorbar below the plot
    norm = Normalize(vmin=0, vmax=100)  # Adjust according to your maximum color difference
    sm = ScalarMappable(cmap='Greys', norm=norm)

    # Adjust layout to include colorbar below the plot
    fig.tight_layout(rect=[0, 0, 1, 0.95])  # Adjust the bottom margin as needed

    # Show the plot
    plt.suptitle("Circular Color Difference : Accuracy of trials across Sheets", fontsize=20, y=1.02)
    plt.show()


5. Variance Calculation

In this part, we are trying to narrow down our approach to this problem, by finding the most efficient trial, that is least variant trial, or the trial that has minimum variance.

5.1 Find Variance for Each Trial/Sheet

This function, "calculate_variance_for_sheet", helps us understand the diversity of colors across a collection of color cards. Imagine each color card as a collection of numbers that describe its color: how bright it is (L), how much red or green it has (a), and how much blue or yellow it has (b). This function looks at all these numbers from all the color cards in a sheet, calculates how much these numbers vary from card to card, and then gives us a single number that tells us, on average, how much the colors differ across all the cards. This helps in understanding if the colors in the group are mostly similar or if there's a wide range of different colors present. It's like measuring 'how consistent or varied the colors are in a set of card in the particular sheet.


    def calculate_variance_for_sheet(sheet):
    lab_values = []
    for card_df in sheet.values():
        lab_values.append(card_df[['L', 'a', 'b']].values)  # Extract L*a*b* values from DataFrame

    lab_values = np.concatenate(lab_values, axis=0)
    return np.var(lab_values, axis=0).mean()
    

5.2 Find the Sheet with minimum Variance

It calculates the variance of color properties for each sheet, finds the sheet with the minimum variance, and then prints its identifier (min_variance_sheet_id) along with variance values for all sheets. Finally, it retrieves the color cards from this sheet (min_variance_sheet) for further examination or presentation. This helps in determining which group of color cards shows the most consistent colors across the dataset. As a result we find the sheet Number 6 is the most consistent trial amongs all the 13 trials


    min_variance_sheet_id = min(variances, key=variances.get)

    print(f"Sheet with minimum variance is: {min_variance_sheet_id}")
    print(f"Variance values for each sheet: {variances}")

    # Accessing the sheet with minimum variance
    min_variance_sheet = sheets[min_variance_sheet_id]

Output


    Sheet with minimum variance is: 5
    Variance values for each sheet: {'0': 313.98876421195024, '1': 311.23875731943366, '2': 314.87699874555045, '3': 315.02180117684867, '4': 308.6178636086258, '5': 303.3818061495981, '6': 306.7759366134873, '7': 310.5400739589045, '8': 318.49616522486843, '9': 309.8834551776406, '10': 309.16972715804945, '11': 310.8522839363206, '12': 307.5054587806306}
    

5.3 Normalised Efficiency Distribution of 42 Cards from Sheet 6 with MasterCard

The Intution Behind Normalised Efficiency Distribution :

After we have found the most consistent trial sheet, we want to visualise how the each 42 cards was close to the MasterCard, in other words, How the efficiency of the 42 cards distributed so that we can haveidea of Trial Progession in the SHeet Number 6. It is worth to note, that as the trial approach to the end, the chance of error diminish for the colors near to the Gap, but this is not strongly consistent for the colors on the far edge.


for i in range(8):
    for j in range(8):
        delta_e_values = []
        color_block_master = master_card.iloc[8*i + j]
        for color_card in least_variant_sheet.values():
            color_block_other = color_card.iloc[8*i + j]
            L_master, a_master, b_master = color_block_master['L'], color_block_master['a'], color_block_master['b']
            L_other, a_other, b_other = color_block_other['L'], color_block_other['a'], color_block_other['b']
            delta_e_values.append(calculate_delta_e(L_master, a_master, b_master, L_other, a_other, b_other))

Data Normalisation


    # Normalize ΔE values within each square
    normalized_delta_e_values = (delta_e_values - np.min(delta_e_values)) / (np.max(delta_e_values) - np.min(delta_e_values))
                        

Visualisation

5.4 Heatmap Visualisation of Accuracy of trials in Sheet No. 6

The Purpose of Heatmap Visualisation to find acurracies of each 42 cards

This part compares color differences between a master color card and multiple trial color cards in a specific sheet. It calculates the differences for each color block in an 8x8 grid using the Delta E formula, which measures the Euclidean distance between color values. Each trial card's differences are visualized as heatmaps, highlighting the accuracy of the trials compared to the master card. The heatmaps for all cards in the sheet are plotted together, with a color bar indicating the magnitude of color differences, providing a clear visual representation of how closely each trial card matches the master card.

Again it is worth to note that the sqaure at the position (6,2) has largest del E, these signify that this is square is the significantly of different color than MasterCard's square at the same position.


Code


    def plot_heatmaps_for_sheet(sheet, master_card):
    fig, axes = plt.subplots(6, 7, figsize=(21, 18))
    axes = axes.flatten()
    max_delta_e = 0
    for idx, card_id in enumerate(sheet):
        delta_e_matrix = np.zeros((8, 8))
        color_card = sheet[card_id]
        for i in range(8):
            for j in range(8):
                color_block_master = master_card.iloc[8*i + j]
                color_block_card = color_card.iloc[8*i + j]
                L_before, a_before, b_before = color_block_master['L'], color_block_master['a'], color_block_master['b']
                L_after, a_after, b_after = color_block_card['L'], color_block_card['a'], color_block_card['b']
                delta_e_matrix[i, j] = calculate_delta_e(L_before, a_before, b_before, L_after, a_after, b_after)
                if delta_e_matrix[i, j] > max_delta_e:
                    max_delta_e = delta_e_matrix[i, j]
        sns.heatmap(delta_e_matrix, cmap='hot', cbar=False, ax=axes[idx], annot=False, vmin=0, vmax=max_delta_e)
        axes[idx].invert_yaxis()  # Invert the y-axis

        axes[idx].set_title(f'Card {card_id}')
        axes[idx].axis('off')

    # Add a colorbar below the plot
    cbar_ax = fig.add_axes([0.3, 0.02, 0.4, 0.02])  # Position: left, bottom, width, height
    norm = plt.Normalize(vmin=0, vmax=max_delta_e)
    sm = plt.cm.ScalarMappable(cmap='hot', norm=norm)
    sm.set_array([])
    fig.colorbar(sm, cax=cbar_ax, orientation='horizontal', label='Color Difference (ΔE)')
    plt.suptitle("Accuracy of trials in Sheet No. 6", fontsize=25, y=1)
    plt.tight_layout(rect=[0, 0.05, 1, 1])
    plt.show()

Heatmap Visualisation of Acurracy

Image

6. Find the Card most likely to be Mean Card from most consistent Trial Sheet - 6

In this section we want to further narrow down out approac. Out of sheet number 6, We want to find the card that has the (L,a,b) values as close as as possible to the Mean Card. This is done by calculating the Euclidian distance between the (L,a,b) values of the Mean Card and the (L,a,b) values of each 42 card in the sheet. The card with the smallest distance is the most likely to be the Mean Card with Mean dataframe of (L,a,b) values across (8*8) = 64 Squares.

6.1. Step - 1 : Calculate the Mean of Sheet-6

This step calculates the average color values (L*, a*, and b*) for each of the 64 color blocks across all color cards in a sheet. It iterates through each card in the sheet, extracting the L*, a*, and b* values for all blocks and appending these values to the respective lists. Once all values are collected, the function stacks these lists horizontally, effectively organizing all L*, a*, and b* values for each block into columns. It then computes the average of these values for each block. Finally, the function creates a new table (DataFrame) containing the average L*, a*, and b* values for each of the 64 color blocks and returns this table, providing a comprehensive overview of the average color properties for the sheet.

Code


    # Function to calculate mean L*, a*, b* values for each of the 64 rows across all cards in a sheet
    def calculate_mean_for_sheet(sheet):
        # Initialize lists to store the L, a, b values for each row
        L_values = []
        a_values = []
        b_values = []

        # Loop through each card in the sheet and extract the L, a, b columns
        for card in sheet.values():
            L_values.append(card['L'].values)
            a_values.append(card['a'].values)
            b_values.append(card['b'].values)

        # Stack the values horizontally and calculate the mean for each row
        L_values = np.stack(L_values, axis=1)
        a_values = np.stack(a_values, axis=1)
        b_values = np.stack(b_values, axis=1)

        L_mean = np.mean(L_values, axis=1)
        a_mean = np.mean(a_values, axis=1)
        b_mean = np.mean(b_values, axis=1)

        # Create a DataFrame to store the mean values
        mean_df = pd.DataFrame({
            'L': L_mean,
            'a': a_mean,
            'b': b_mean
        })

        return mean_df

6.2. Step - 2 : Find the Card closest to the Mean of Sheet-6

This stepidentifies the color card that is closest to the average color values in sheet '6'. It iterates through each card in the sheet and calculates the distance between the card's color values and the average color values for the sheet. This distance is measured using a pre-defined method. During this process, the code keeps track of the card that has the smallest distance to the average. After comparing all the cards, the code determines which card is closest to the mean color values and outputs the identifier of this card along with the distance. This helps in identifying the card that best represents the average color properties of the sheet.

Code


for card_id, card in sheets['5'].items():
  # Calculate the distance to the mean DataFrame
  distance = calculate_distance(card[['L', 'a', 'b']], mean_df_sheet_5)

  # Update the closest card if the current distance is smaller
  if distance < min_distance:
      min_distance = distance
      closest_card = card_id

print(f"The card closest to the mean in sheet '6' is card '{closest_card + 1}' with a distance of {min_distance:.4f}")

Output


    The card closest to the mean in sheet '6' is card '23' with a distance of 0.4511

6.3 Visualise the Card 23 from Sheet 6 || The Most likely to Be Mean from the most Consistent Sheet

Let's see how our select card looks like ..


A very spontaneous difference can be observed, that is the color of the Square (6,2) of the card 23 is very different from the MasterCard's one

6.4 Visualise Circular Difference between the Card 23 from Sheet 6 with MasterCard


The intution behind plot

In the following plot, del E is being calculated for the Card 23 from Sheet 6 with MasterCard. Now as an observer may be intersted to know huw intensely, each color square differ from each other, so for that intensity of delta E has been calculated and visualised in the rectangle, background of the Overlapped Circular Blocks, where left one is the from card 23 from Sheet 6 and right one is the MasterCard. This gives an idea of the most accurate color square out of 64 squares.


Explanation

The plot visualizes the color differences between "Card 23" from "Sheet 6" and the reference "MasterCard." It uses background intensity and color overlays to highlight discrepancies. The intensity of each cell's background is based on the color difference, calculated using Euclidean distance in the L*, a*, b* color space, and normalized to a maximum value of 100. This grayscale background, where darker shades indicate higher color differences, provides a clear indication of color variance. Overlaid on each cell are two circles representing the actual colors from the ideal card and the MasterCard, with colors converted from L*, a*, b* values to RGB for accurate visualization. The gridlines enhance the clarity of individual sections, and a color bar labeled "Color Difference Intensity" offers a reference scale. The Y-axis is inverted for standard visual orientation. This combined approach of intensity mapping and color overlays offers a comprehensive view of color discrepancies, crucial for quality control in color-critical applications. The plot effectively identifies areas of significant color variance, aiding in a detailed analysis of color accuracy between the cards.

6.3 Closest Cards to Mean Visualization

The code calculates average L*a*b values for each row of cards on every sheet. It then finds the card on each sheet that is closest in color to this average to measure the color difference. Additionally, it compares the average color of a "master" card to the averages of all sheets, identifying which sheet's colors are most similar to the master. The results show which card on each sheet matches its average color best and which sheet overall is closest to the master standard.

To visualize the results, a bar chart is used where each bar represents a sheet. Shorter bars indicate that the cards on that sheet closely match its average color,showing higher color consistency.

colord-Histogram

7. Conclusion

In order to determine how closely the printed colors matched the intended values, we carefully examined the color measurements from Douglas color cards for this project. We converted and showed the raw MasterColorCard and LabMeasurements-Color-Card data, starting with data reading and preprocessing. This made it easier for us to spot inconsistencies and examine color dispersion within and between sheets. Through the process of calculating variances and viewing color differences (ΔE values), we were able to obtain an understanding of the accuracy and consistency of the printed colors. The attached figure illustrates our approach, which demonstrates the painstaking measures we took to transform and analyze the data in order to guarantee a thorough grasp of color behavior and print quality. The analysis showed areas where the printing process might be improved and offered insightful information on the color accuracy and uniformity of the printed cards.