Welcome to week 4 of Introduction to Data, Signal, and Image Analysis with MATLAB. In this week, we will be looking at how we can use MATLAB for image analysis. Our first lesson is on image representation in MATLAB. By the end of this lesson, you will understand how to read and write different types of images, display an image, and inspect and manipulate image pixel values in MATLAB. Let's first visit what an image is and how it is represented in MATLAB. As we will see, an image is nothing more than a matrix of numbers. Each number represents the intensity of a color of a pixel, and the row and column position of the numbers in the matrix determine the y and x location where the corresponding image pixel is located in the image. Let's look at an example. Here is a grayscale image of a cameraman. An image is simply a 2D matrix of so-called pixels, where the word pixel as a shortened version of the term picture element. This image is composed of 256 by 256 grayscale pixels. Each pixel is a square that is filled with a single color. A grayscale image only contains grayscale colors, that is different shades of gray, ranging from black to white. From a far, it is difficult to tell that this image is composed of 256 discrete row elements and 256 discrete column elements. It appears instead to be rather continuous. But let's zoom in on this region containing the camera and cameraman's face. This is approximately a 50 by 50 set of pixels where we can start to see some jagged pixelation effects. But it's still somewhat subtle. Let's zoom in even further. This region containing just the cameraman's mouth and part of the camera. This is a 10 by 10 region of image pixels. At this magnification, it is easy to see the pixelation of the image into discrete elements. From a far, each pixel is so small that they appear to form a continuous picture. But viewed up close, we may wonder how we can be fooled into thinking this actually looks like a face in camera was such a course discrete representation. Each pixel represents a single color, in a grayscale image, each color is a different shade of gray, which can be represented with a single number. A large number would represent a brighter shade of gray closer to white. A small number would represent a darker shade closer to black. Historically, digital images tax to the memory resources of computers, thus, digital image scientists established memory efficient formats and conventions for representing digital images. They aimed to balance the trade-off between maintaining image quality and data storage capacity. This is not as much of an issue with modern computers. However, traditional conventions on how to store digital images remain pervasive today. To this end, 8-bit unsigned integers are typically used to represent shades of gray. With an 8-bit unsigned integer, we can represent integers from 0-255. This gives us 256 discrete shades of gray, zero is black, 255 is white, and each integer between is a different shade of gray. Having only 256 options. As a result of choosing such a compact 8-bit representation. A 16-bit integer, for example, would permit representing over 65,000 shades of gray, but would make storing the image required twice as much memory. In practice, 256 shades is enough for a reasonably good image fidelity, and thus this convention has remained even though memory availability on modern computers has expanded exponentially. Looking at our 10 by 10 set of pixels, we can also show them as a matrix of numbers. Here's a printout from the MATLAB command window of this 10 by 10 portion of the image matrix. As we can see, the matrix is of data type uint8, which stands for unsigned 8-bit integer, permitting us to represent integers between 0-255. If we look at the lower left part of the image, we see this is a relatively bright region of the image. These pixels are assigned a bright shade of gray, and then the corresponding region of the image matrix, we see relatively large integers within our 8-bit unsigned integer range with numbers close to 200. On the other hand, if we look at the right side of the image patch, we see relatively dark pixels. These pixels are assigned a darker shade of gray. When we look at the corresponding region of the image matrix, we see low numbers of around 50. We can see that really an image is just a matrix of numbers. The picture we draw on the screen is just a way to visualize the set of numbers. In this sense, an image can be considered to be a two-dimensional signal. As we saw with signals, they are a set of numbers of a process that varies over time. An image is a set of numbers of a process that varies over two spatial dimensions, the rows and columns of the image. Thus many image processing and analysis methods are two-dimensional generalizations of 1D signal analysis algorithms. Color images, as we will see, require three numbers per pixel to represent different colors, as opposed to the single number we need for the gray-scale value. Another convention from early digital imaging that remains is the coordinate system that is conventionally used to define the pixel positions of an image. Traditionally, pixel coordinates are defined identically to matrix coordinates. We use the row column convention. This means that pixel 1,1, that is, the first row and first column pixel, will fall at the upper left corner of the image. The first coordinate, the row coordinate, increases as we move down the rows of image pixels. The second coordinate, which is the column coordinate, increases as we move right across columns of image pixels. When we visualize the image in a figure window, it is important that we explicitly note this is very different from the x,y coordinate system we use for plotting. With the standard x,y coordinate system, x is the first dimension and it increases to the right on the horizontal axis. Y is the second dimension, and it increases up the vertical axis. If we think of the image is having x, y coordinates, with x being the first dimension and y being the second, then when we display an image, our first dimension x increases downward along the vertical axis, and our second dimension y increases rightward along the horizontal axis. This may seem a bit odd at first, but again, this follows our matrix coordinate notation. When we index a matrix, our first dimension indexes down the rows of the matrix and our second indexes across the columns. We've touched on our preliminaries, lets go to MATLAB and look at some examples. First, since an image is just a matrix of numbers, we can just make a synthetic image on the fly very easily. Let's say we want to create a 10 row by 15 column image. We define a rows variable r be equal to 10. Our columns variable c to be 15. We initialize our image as a matrix of zeros with shape r by c. Next we can use nested for loops first for i equal 1 to r, then for j equal 1 to c, and set entry i, j in our image matrix to be equal to i times j. This will fill in the image such that the value of a pixel is equal to the row number for that pixel multiplied by the column number for that pixel. Printing into the screen. We see it's just a matrix of numbers where the first pixel will have a value of one in the upper left corner of the image matrix, and the bottom right corner and will be the brightest region with a value of 10 times 15 equals 150. To visualize this matrix as an image, we can use the image sc command. This displaced the matrix as an image and scales the values such that the largest numbers are shown as the brightest pixels, and the smallest numbers are shown as the darkest pixels. By default, MATLAB maps the numbers in the matrix to colors. To see what colors MATLAB is assigning each number to, we can use the colorbar function, and MATLAB will display a legend in the figure window that shows which colors each number is mapped to. We can see that by default, the largest number in the matrix is displayed as yellow and the smallest numbers are displayed as blue, with green being mid-range numbers around 80. To switch to a more conventional gray-scale representation, we can type colormap(gray), which will change us to a gray-scale representation. As we can see now, the smallest numbers are black, 150, our largest number is white, and numbers between our different shades of gray. Let's also label our axes. As discussed before, when displaying an image, our first dimension, the rows of the matrix, is on the y-axis. So we y-label our vertical axis as rows. Our second dimension, the columns of the matrix is on the x-axis. So we x-label our horizontal axis as columns. Looking at the matrix and image side-by-side, again, we can appreciate that a digital image is really just a matrix of numbers. The image we display is just a way to visualize this matrix that is more pleasing and intuitive to our eyes than looking at a ROM matrix of numbers. Another way to generate an image matrix is with random numbers. For example, let's consider defining an image using rand. This will give us a matrix of random numbers of size r by c and we can display it using imagesc and label the rows and columns. We can see that using random numbers to create an image results in a static noise appearance image like this. Next, let's load a photographic image. Let's load in the built-in cameraman image from MATLAB. MATLAB's imread function can load many different standard image file types, such as PNG, TIF, JPEG, and BMP images. The corresponding write function is imwrite, which supports the same file types. We define image as the output of the imread function, where the argument is the file name. The cameraman image is built into MATLAB's file path and it's titled cameraman.tif. The.tiff extension tells MATLAB to load it as a TIF file type. We can see now in our workspace, we have a variable image, which is a 256 by 256 matrix of type uint8. We can display it using the imagesc function and we can see it show up in our figure window. However, depending on the size of our figure window, the image may look a bit strange. That's because by default, the aspect ratio of the image may not be correct. MATLAB is stretching the horizontal axis relative to the vertical axis to make the image fill the figure window in a certain way. This makes our pixels no longer square, but rather rectangles that are longer in the horizontal axis than the vertical one. If we want the pixel spacing in the horizontal and vertical axes to be equal to each other, we use the axis equal command. Now, the displayed distance in the horizontal and vertical axes are equal, and we see the image with square pixels. Now, let's say we would like to somehow manipulate this image and save it to a new file. Perhaps this photo is evidence of this man taking a picture of a crime. The prosecutor is using that picture as evidence and the cameraman is a crucial witness. Clearly, we urgently need to anonymize this image for his protection. We can do this by changing the entries in the matrix that correspond to the pixels defining his face. We can use the zoom tool and MATLAB, to zoom in on the face region. We see the face exists in rows 40-75 and columns 100-135. Since the image is just a matrix of numbers, we can go to our image, row is 40-75, and columns 100-135, and change the numbers that are in there. We can simply set all the pixels to value 127. This should correspond to a mid-grade pixel intensity. We again, display our image using imagesc and our axis equal to put it in the correct aspect ratio. So we can see we have manipulated our image. We have effectively deleted the cameraman's face from the image. Now, this image can be safely stored on our server without worrying about the witnesses identity being revealed. Let's save it as a JPEG. We call imwrite, with the arguments image, followed by the filename we wish the image to have, we shall call it cameraman_anonymized and we put in the extension that we desire. Here I'll use JPEG. When we include the extension in the file name, imwrite does not need additional arguments to know what format in which to write the file. Now, we've created a new image file that can be loaded into any other image display software that can load JPEG image types. To see a full list of the different image data types supported, consult the MATLAB help files on imread and imwrite.