Skull Shape Analysis


As a coding enthusiast, BE100 – Problem-Solving in Biological Sciences and Engineering was the class I enjoyed most during this fall quarter. The course focused on applying MATLAB to real biomedical problems, such as writing code to convert DNA sequences into amino acid chains. Through these assignments, I learned how to plot data, perform operations with matrices, and analyze biological information. All these skills eventually came together in our final project, where we basically…compare skulls. It’s obviously more than that … read on! This was technically a group project as we were supposed to write the code on our own but we could get assisted from the people at our table if we happened to be stuck. For that reason, I will acknowledge that help and use the pronouns “we/our/us” to describe the process.

What is the code doing exactly?


Overall, our code collects the skull contour, nose, and eye points from each of the given skull images. Following this pattern:


Using the points we collected, we plot the original, translated, scaled, and rotated coordinates for the skulls to then determine the smallest/largest Procrustes distance between each of the skulls and Skull One to reveal which skull is most/least like Skull One.

With all the needed data generated, we collected the Procrustes distance between each of the skulls and Skull One to determine which skulls were the most similar/different.

While making this project, we learned and applied our knowledge of reading/writing Excel files, reading/showing images, and plotting. We had to read and show each of the given skull image files and then write an Excel file to store coordinates. To plot these coordinates (and their translated, scaled, and rotated counterparts), we had to read the Excel files specified by the user. More importantly, it shows that eyeballing or judging with intuition can be wrong; and to be as accurate as possible, scientific and complicated data analysis should assist in generating the right outcome. These are all skills that we learned separately through reading sample code and doing individual assignments, but this project helped us see how these three important skills can be integrated into each other. Developing these skills of making various functions that will then be integrated into a larger function will be helpful in our future BE classes, where we might be asked to analyze lab data.

An extra feature we added to the code was expanding the subplot to include 6 sections instead of 4. The two additional sections at the beginning show the two skulls being compared in the subsequent plots. We decided to add this feature because when we ran the code, we kept looking back at the image for the corresponding skull to see if the provided Procrustes distance looked reasonable. To make the program more user friendly, we added these images so the user would not have to flip back to their files to look at the corresponding skulls when running the code. Everything required for this addition is included in the image of code below from the Skulls_CompareSkulls file and produces the below image.

Skulls_CompareSkulls.m
% Before running this script, you should run your
%   Skulls_GetSkullPointsFromImage.m
% script at least twice,
% thus creating Excel files for at least two of the skull images.

clear
clc

% Open a dialog by which the user can select the name of an Excel file
% that is intended to contain the 18 points from an image of a skull.
% Store the full pathname for the file in a variable called something like

[fileName, filePath] = uigetfile('*.xlsx', 'Select a file to read ...');
skull_1_file = fullfile(filePath, fileName);

% Repeat the previous step, that is, again open a dialog
% by which the user can select the name of an Excel file
% that is intended to contain the 18 points from an image of a skull.
% Store the full pathname for the file in a variable called something like
%   skull_2_file.
[fileName, filePath] = uigetfile('*.xlsx', 'Select a file to read ...');
skull_2_file = fullfile(filePath, fileName);

% Read the two Excel files into variables something like
original_skull_1_matrix = readmatrix(skull_1_file);
original_skull_2_matrix = readmatrix(skull_2_file);

% Close all existing figures and start a new figure.
close all

% In the upper-left of a 2 x 2 subplot with title 'Original skulls',
% call  Skulls_PlotSkull  twice to plot:
%   - original_skull_1_matrix in blue
%   - original_skull_2_matrix in red
% (Choose whatever line widths you think look good.)
figure

%This code below is part of my additional piece of the project:------------ 
image_1_filename = replace(skull_1_file, 'xlsx','jpg');
image_2_filename = replace(skull_2_file, 'xlsx', 'jpg'); 

subplot(2, 3, 1)
hold on 
dataOne = imread(image_1_filename); %Where do we get this file!!!?
imshow(dataOne)
title('Skull We are Comparing (blue)')

subplot(2, 3, 2)
hold on 
dataTwo = imread(image_2_filename); %Where do we get this file!!!?
imshow(dataTwo)
title('Choosen Skull (red)')
%--------------------------------------------------------------------------

subplot(2, 3, 3)
hold on % Not needed for these subplots, but needed in general
Skulls_PlotSkull(original_skull_1_matrix, 'b', 3)
Skulls_PlotSkull(original_skull_2_matrix, 'r', 3)
title('Original Skulls')

% Call  Skulls_TranslatePolygon   twice to get two new matrices,
% each of which translates one of the original matrices so that
% its centroid is moved to (0, 0),
% storing them in variables named something like:
translated_skull_1_matrix = Skulls_TranslatePolygon(original_skull_1_matrix);
translated_skull_2_matrix = Skulls_TranslatePolygon(original_skull_2_matrix);

% In the upper-right of a 2 x 2 subplot with title 'Translated skulls',
% call  Skulls_PlotSkull  twice to plot:
%   - translated_skull_1_matrix in blue
%   - translated_skull_2_matrix in red
% (Choose whatever line widths you think look good.)
subplot(2, 3, 4)
hold on 
Skulls_PlotSkull(translated_skull_1_matrix, 'b', 3)
Skulls_PlotSkull(translated_skull_2_matrix, 'r', 3)
title('Translated Skulls')

% Call  Skulls_ScalePolygon   twice to get two new matrices,
% each of which scales one of the translated matrices so that
% its size is 1,
% storing them in variables named something like:
scaled_skull_1_matrix = Skulls_ScalePolygon(translated_skull_1_matrix);
scaled_skull_2_matrix = Skulls_ScalePolygon(translated_skull_2_matrix);

% In the lower-left of a 2 x 2 subplot with title 'Scaled skulls',
% call  Skulls_PlotSkull  twice to plot:
%   - scaled_skull_1_matrix in blue
%   - scaled_skull_2_matrix in red
% (Choose whatever line widths you think look good.)
subplot(2, 3, 5)
hold on 
Skulls_PlotSkull(scaled_skull_1_matrix, 'b', 3)
Skulls_PlotSkull(scaled_skull_2_matrix, 'r', 3)
title('Scaled Skulls')

% Call  Skulls_BestRotatedPolygon,
%   sending it the  scaled_skull_matrix_1  as the reference skull
%   and the  scaled_skull_matrix_2  as the skull to rotate
% storing the result in a variable named something like:
rotated_skull_2_matrix = Skulls_BestRotatedPolygon(scaled_skull_1_matrix, scaled_skull_2_matrix);


% In the lower-right of a 2 x 2 subplot with title 'Rotated skulls',
% call  Skulls_PlotSkull  twice to plot:
%   - scaled_skull_1_matrix in blue
%   - rotated_skull_2_matrix in red
% (Choose whatever line widths you think look good.)
subplot(2, 3, 6)
hold on 
Skulls_PlotSkull(scaled_skull_1_matrix, 'b', 3)
Skulls_PlotSkull(rotated_skull_2_matrix, 'r', 3)
title('Rotated Skulls')