1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
"""
Introduction to Traits
======================
To make the tutorial more practical, let's imagine that you are
writing some tools to organize and process image data, perhaps
to help you develop a machine learning algorithm, or to document
your research. This image data might come from a camera, a scanning
electron microscope, or from some more exotic source. No matter the
source, the image will have data associated with it, such as who
produced the image, the equipment that was used, the date and time
that the image was taken, and so forth.
In this tutorial, we'll consider an example where we have a collection
of greyscale SEM images, each of them stored in a file, along with other
information, such as the sample that was imaged, the size of the scanned
region, and the operator who took the image.
We would like to read in the image and provide a number of standard
analysis steps to the user, as well as making the raw image data
available as a NumPy array.
The sample code here shows how you might create a class in standard
object-oriented Python with NumPy for analysis and Pillow for image
loading. Experiment with the code in the example to see how you can use it.
In the subsequent steps of the tutorial, we'll look at how we can use Traits
to simplify and improve the code here.
"""
import datetime
import os
import numpy as np
from PIL import Image as PILImage
class Image:
""" An SEM image stored in a file. """
def __init__(self, filename, sample_id, date_acquired, operator,
scan_size=(1e-5, 1e-5)):
# initialize the primary attributes
self.filename = filename
self.sample_id = sample_id
self.operator = operator
self.date_acquired = operator
self.scan_size = scan_size
# useful secondary attributes
self.scan_width, self.scan_height = self.scan_size
def read_image(self):
""" Read the image from disk. """
pil_image = PILImage.open(self.filename).convert("L")
self.image = np.array(pil_image)
# compute some extra secondary attributes from the image
if self.image.size > 0:
self.pixel_area = (
self.scan_height * self.scan_width / self.image.size
)
else:
self.pixel_area = 0
def histogram(self):
""" Compute the normalized histogram of the image. """
hist, bins = np.histogram(
self.image,
bins=256,
range=(0, 256),
density=True,
)
return hist
def threshold(self, low=0, high=255):
""" Compute a threshold mask for the array. """
return (self.image >= low) & (self.image <= high)
# ---------------------------------------------------------------------------
# Demo code
# ---------------------------------------------------------------------------
this_dir = os.path.dirname(__file__)
image_dir = os.path.join(this_dir, "images")
filename = os.path.join(image_dir, "sample_0001.png")
# load the image
image = Image(
filename=filename,
operator="Hannes",
sample_id="0001",
date_acquired=datetime.datetime.today(),
scan_size=(1e-5, 1e-5),
)
# read the image from disk
image.read_image()
# perform some sample computations
print(
"The maximum intensity of {} is {}".format(
image.sample_id,
image.histogram().argmax(),
)
)
pixel_count = image.threshold(low=200).sum()
print(
"The area with intensity greater than 200 is {:0.3f} µm²".format(
pixel_count * image.pixel_area * 1e12
)
)
|