Convex Hull

In this post we will learn a few things about a convex hull.

Before we get into the main subject I would like to chat for a few on a different subject. Currently I continue to read Natural Language Processing by Ekaterina Kockmar. Earlier this morning I was reading the section 3.2.2 Matching forms of the same word: Morphological processing. In the current chapter we are trying to develop an information retrieval system. In a nutshell we have a set of documents of interest and a set of queries. The idea is that given a query we want to return relevant documents in descending order. Sounds very much like what Google search does. Of course the objectives are not to write something to compete with searches on web browsers, but to give us an idea of the general steps needed to perform such a task.

In particular I was reading about Stemming. The idea is that when you have words in a query and wish to map them to words on a document, the forms of a word may be different. A simple word match would not work (e.g., continue and continuation) because for a computer the words are different. If we implement an algorithm using stemming we will be able to match the words.

As I was reading the section an old Spanish saying (https://en.wikipedia.org/wiki/Saying) “The devil knows more through being old than through being a  devil.” If you stop for a few and think about the Spanish saying you should reach the conclusion that it is wrong. In general if you do not reflect on what happened, the good and bad decisions you made, you will not learn and therefore you will not become wiser. For the Spanish saying to be true one must assume that the devil reflects on past events. Since the devil is a personification of evil and not a being like us, the saying is just a saying.

That said, when engineering software, we need to make sure to take time reading about the problem at hand. In most cases similar, if not the same problem might have come up and there might be solutions that one can use directly or as a base for a solution for your specific problem. Learning and experimenting (within reason) allows us to get to the simplest solution which does not incur side effects. A quick solution might become very long and expensive if not properly designed and tested. I always think about the KISS principle when engineering a solution.

Enough chat, let’s get to the main subject of this post.

In this post we will learn and experiment with the concept of a convex hull. In a nutshell if you have an image the associated convex hull is the smallest convex polygon that contains all points in the image of interest.

In this post we will use VSCode with GitHub Copilot and the scripting language Python. I should disclose that at the time of this writing I am a Microsoft employee and have been using VSCode for several years and Copilot for a few months.

# **** folder of interest ****
cd C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos

# **** open file of interest using VSCode ****
(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>code ConvexHull.py

# **** execute python script of interest ****
(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>python ConvexHull.py

We will use the Anaconda Prompt. We start by getting to the folder of interest. At that point we invoke VSCode with the specified Python script file. At this point the file does not exist in the file systems so we go to VSCode and save the file. The following line executes the Python script of interest.

# **** ****
import matplotlib.pyplot as plt                     # Plotting

# **** ****
from skimage.morphology import convex_hull_image    # Convex Hull
from skimage import data, img_as_float              # Image
from skimage.util import invert                     # Invert Image


# **** read horse_image ****
horse_image = data.horse()

# **** invert horse_image ****
horse_inverted = invert(data.horse())

# **** ****
fig, axes = plt.subplots(   1, 
                            2, 
                            figsize=(12, 8),        # width, height in inches
                            sharex=True,            # x-axis will be shared among all subplots
                            sharey=True)            # y-axis will be shared among all subplots

# **** flatten axes ****
ax = axes.ravel()

# **** plot horse_image ****
ax[0].set_title('horse_image')
ax[0].imshow(   horse_image,
                cmap='gray')

# **** plot horse_inverted ****
ax[1].set_title('horse_inverted')
ax[1].imshow(   horse_inverted,
                cmap='gray')

# **** fit plots within your figure cleanly (an alternative to tight_layout is constrained_layout) ****
fig.tight_layout()

# **** display the plots****
plt.show()

With this code we will display the image of the horse followed by the inverted image of the same horse. We do this in preparation of generating the convex hull.

# **** convex hull of horse_inverted ****
covexhull_horse = convex_hull_image(horse_inverted)

fig, axes = plt.subplots(   1,
                            2,
                            figsize=(12, 12),
                            sharex=True,
                            sharey=True)

# **** flatten axes ****
ax = axes.ravel()

# **** plot horse_inverted ****
ax[0].set_title('horse_inverted')
ax[0].imshow(   horse_inverted,
                cmap='gray',
                interpolation='nearest')

# **** plot convex hull of horse_inverted ****
ax[1].set_title('convex hull of horse_inverted')
ax[1].imshow(   covexhull_horse,
                cmap='gray',
                interpolation='nearest')

# **** fit plots within your figure cleanly (an alternative to tight_layout is constrained_layout) ****
fig.tight_layout()

# **** display the plots ****
plt.show()

We create a convex hull surrounding the inverted image of the horse.  The inverted horse and associated inverted horse images are then displayed side by side.

# **** ****
convexhull_diff = img_as_float(covexhull_horse.copy())
convexhull_diff[horse_inverted] = 2                     # 2: white

# **** ****
fig, ax = plt.subplots(1, 1, figsize=(12, 8))           # width, height in inches

# **** ****
ax.imshow(  convexhull_diff,
            cmap='gray',
            interpolation='nearest')
ax.set_title('Difference between inverted and convex hull image')
plt.show()

In this code, we plot the inverted horse and the convex hull. As we can see the hull is the smallest polygon that completely encloses the image of the horse.

Hope you enjoyed this post. There is a lot to learn and experiment with. The idea is to just get a flavor of what can be done with images and Python using scikit-image.

Remember that one of the best ways to learn is to read, experiment, and repeat. In this case I believe there are a lot of repetitions in order to experiment with the subject at hand.

If interested in this code, you can find it in my GitHub repository.

Enjoy,

John

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.