 Good morning. Today we are going to discuss some more applications on multidimensional arrays. More specifically, we will start by looking at the simpler problem of matrix multiplication and then we will look at a special matrix called magic square. Finally, we will discuss an important and useful and interesting application of two-dimensional arrays in representing digital images or digital photographs with which most of you would be familiar. We have specifically discussed the notion of histogram of a digital image, histogram equalization techniques and how do you get a better quality image after doing that processing. So, this is the matrix multiplication problem. All of you are familiar with matrix multiplication. So, the two matrices which need to be multiplied need to be multiplication compliant, meaning that there should be a common dimension. If one matrix is m by n, the other matrix should be n by p. That is, number of columns in one matrix should be same as number of rows in the second matrix and the resultant matrix will be m by n. So, essentially if you have a matrix let us say with four columns and three rows and if you have another matrix which you are multiplying it with which has let us say two columns and four rows, then what will be the size of the resultant matrix four rows and two columns. So, essentially you will the way you will calculate is that if you are calculating let us say this element, then this element is row number 0, 1 and 2 and column number 0 and 1. So, the way you will calculate is you will take the row number 2, 0, 1 and 2 of the first matrix, multiply each element with the corresponding element of column number 0. So, you multiply the 0th element with 0th element first element and you sum them. Typically, call the inner product of two vectors, you will replace the resultant element by that value and that will be the multiplication. So, this is a pretty straightforward example and this will be the segment of the program which will do that. I am running two nested iterations, first I vary i from 0 to m minus 1 which will move over rows respectively, within that I will move I will I will iterate on j equal to 0 to p minus 1. So, it will effectively iterate around columns and finally, so these rows and columns are the rows and columns of the resultant matrix for each of which I will be calculating a value i comma j and the way the value is calculated is I said that value to 0 is like summation set to 0 and then over the common dimension k equal to 0 to n minus 1, I will multiply a i k and b k j and accumulate it in the sum. So, this is how you will get this, the problem and the solution is very simple and straightforward. What I would like to emphasize is that you have to be careful about how you handle the indexes, in general for one dimensional arrays or for two dimensional arrays, it is important to get the iterations set up correctly and within the iterations the use of indices correct. So, it will for example, be incorrect to use here everything like c j i for example, will it be incorrect or will it be correct. Suppose, I said c j i is equal to c j i plus this, will it be correct. Think about it, the best way is to take a simple two by two matrix, another two by two matrix, see what happens when you implement your logic by using an algorithm where indexes are used differently. In general, any use of indexes should be verified by hand computation of a simple example to confirm that your calculations are correct. Incidentally, such an algorithm may work correctly with some additional modification if necessary, but what it will do is it will accumulate the sums differently. We will now look at a magic square, magic square is a square matrix where the sums of all the rows and columns are same. So, for example, if you look at this matrix 8 1 6 3 5 7 4 9 2, this is called a basic magic square. There could be multiple variants of this magic square. For example, if the sums and columns have to add up to the same value by adding a constant value to every term of this matrix, I will still get that proper. Not only that, there is an additional property that the diagonals also must add up to the same. So, one diagonal is the main diagonal 0 0 1 1 2 2, which has values 8 5 and 2. So, these clearly add up to 15, but the other diagonal which is 0 2 1 1 and 2 0 6 plus 5 plus 4 should also add up to 15. These are very historical matrices, Chinese knew this, Indians knew it and subsequently it flew down to the rest of the world. And, of course, the Europeans once they started studying the square matrices, they came up with a whole lot of interesting variations. Again, Wikipedia gives a lot of details about the matrices, how to generate them and so on. So, we are essentially looking at the basic matrix and we wish to determine if a given matrix is a magic square or not. So, here we note the following. An n by n matrix will generally have n rows and n columns and we will have two diagonals. Now, in order to test whether a given matrix is a magic square or not, I should find all these sums. They should all turn out to be equal. If I am required to find out whether a given matrix is a basic magic square, then I know that all the rows and sums, rows and columns will sum to this particular value n into n square plus 1 by 2. This is a well known formula. If the basic matrix is not what we are looking for, but any arbitrary matrix, then what we will have to do? We will have to find out the sum of any one of the rows, for example, and then check whether all other rows, all other columns, all other diagonals add up to the same value or not. That is possible. In this particular case, we will use this formula. So, after calculating the required sum, I will then proceed to calculate the sums of each of the rows, sums of each of the columns, sums of each of the diagonals. If all of them, if any one of them is not equal to the desired sum, obviously the matrix is not a magic square. Now, I know a mechanism of exiting from my program if something is not proper. What is that mechanism? I simply say return one in some condition. So, what I will do is I will calculate the desired sum first, then start calculating row sum, column sum, diagonal sum. At every stage, if the calculated sum does not turn out to be equal to the desired sum, I can simply quit. That means if I proceed further, there is a charge that the matrix is the magic square. Again I check and so on. At the end, if I haven't got out of the program and if I reach some point, then obviously the square matrix is the magic square. I will so announce it. Now, how many rows and how many columns will be there in an n by n matrix? n rows and n columns. Since I want to preserve the sums of each of the rows and each of the columns, I would require arrays of size n for these sums. Obviously, n will be given as an input. So, I can't use it to declare the size and therefore, I choose some maximum size here. So, this is my program. I say int square 20 by 20 capital N which is actually the size and i, j and sum are the temporary value. I declare the integer arrays r sum and column sum and I declare the d 1 sum, d 2 sum as variables which will hold the values of the two diagonal sums. I read the matrix, initialize the r sum and c sum. So, for example, here I get the value of n, then I get the values of all the elements of array square that is two dimensional array and then I initialize this r sum i and j sum i equal to 0. I can actually shorten this code by in the same outer loop that is after finishing this loop. I can put this code inside the outer loop here. I will get the same initialization. Now, this is the first of the loops that I run to calculate the row sums. So, I run it from i equal to 0 to n minus 1, j equal to 0 to n minus 1 again and in the inner iteration here, I accumulate the sum for r sum i. I is varied outside. So, for i equal to 0, for example, this will accumulate the sum for 0th row all columns. If this sum in the same outer iteration of i, if this sum is not required sum, then obviously this square is not a magic square. I so declare and I say return 1. The point to be noted is my program will not proceed further if the first row sum itself or the second row sum or the third row sum is not correct. If I come out here at this point, then obviously there is a chance that the matrix may be still a magic square. So, I go further and do exactly the same thing for column sums. All that I have done is I have used the same thing, but I have inverted the order of j and i. So, I have the outer loop is for j equal to 0 to n minus 1, inner loop is i is equal to 0 to n minus 1 and c sum j that is for a given column j, which is fixed by the outer iteration, i iterate over all the rows i equal to 0. So, this formula remains same. Please note that I could have done the same thing by running a loop first for i then for j, but then accumulating j i. I would have accumulated in a slightly different way. Again, I do the same thing. I check whether it is equal to the desired sub and if not, I declare it is not a magic square. I say return 1, which means I get out. Again, if I come to this point, then there is still a chance that the given matrix is a magic square. Now, I have to calculate the two sums of the diagonals. So, I say for i equal to 0 to n minus 1, d 1 sum plus equal to square i i. What will this calculate? For i equal to 0, it will be 0 0 1 1 2 2. Look at the way I have done this here. For j equal to 0, j equal to n minus 1, less than n minus 1 or less than equal to n minus 1 and j plus plus. I calculate d 2 sum. Now, this is the trick square j n minus j minus 1. So, when j is 0 and let us say n is equal to 3, then what will be this index? 3 minus 0 minus 1, that is 2. So, it will actually take 0 comma 2 element. So, if I have first time, I have calculated the main diagonal, the next time I will calculate the other diagonal. So, 0 2 1 1 and 2 0, that is how I will excess these. Again, the point to be noted is you have to be careful about how you choose your indexes, both for iteration control as also within the expressions that you use, because that will determine whether the collect elements are being excess and are being counted or not. Again, I check the same thing. If these diagonal sums are correct or not, so I check d 1 sum. If that is not so, I get out, then I check d 2 sum. If that is not so, I get out. And if we reach this point, now there is nothing more to be checked. I have checked all row sums, column sums and diagonal sums. So, I declare the square to be a matrix. Observe that all the output statements, not a magic square, not a magic square etcetera, will appear only if a particular sum does not agree with the required sum. And it will appear only once and when it appears, I will exit the problem. So, if no output has come so far, it means that the square is a magic square and I will play this. This is just one point that I would like to discuss with you. I have checked each of these sums to be equal to the desired sum in separate statements. Is that necessary? Can I combine these two conditions? How would I combine these two conditions? Or is d 1 sum not equal to sum or d 2 sum not equal to sum? Is that correct? I deliberately raise this point, because we are talking about combining Boolean conditions. If c 1 is one Boolean condition and c 2 is another Boolean condition, then we want to check that c 1 should be true, which is sum is equal and c 2 is also true should be true, which is the second sum is also equal. So, c 1 and c 2 is what ordinarily I would write if I am checking for equality. I want to do not of this in order to announce that a square is not a magic square. So, when I say not of c 1 and c 2, it translates into not c 1 or not c 2. So, the Boolean translation of a complex logic should be remembered in terms of the Boolean truth tables. But in case of any doubt, you can always write it like this in expanded form and the program will work correctly. Here is an alternate program. Notice what I am doing here. Of course, I have not given the code for reading the n elements of the matrix and initializing this. I am assuming this is done exactly like what we had done in the previous version of the program. Here, I calculate the sum and in a single iteration i equal to 0 to n, j equal to 0 to n minus 1. I calculate r sum plus equal to square i j and c sum i plus equal to square j i. So, the trick is by using square j i, I am actually accumulating the correct column sum. You can confirm it by checking it. I have just shown some sample illustrative example here to see how it works. Similarly, in the outer iteration of i itself, I am calculating d 1 sum and d 2 sum both. So, here is the row how it is added. For a given i, I am adding square i j to r sum. So, if i is equal to 1, then in the inner iteration of j equal to 0, 1 and 2, initially r sum 1 will be 0, then I will add square 1, 0, square 1, 1, square 1, 2. So, adding the entire row number 1 into together and of course, confirming that the value is 50. In exactly the same fashion, when I say c sum i is equal to c sum i plus square j i. Now, I am calculating the column sum. So, if i is equal to 1, then for column number 1, I will vary j from 0, 1 and 2. And as j varies, it being the row index here, it will vary row from 1, sorry, column from 0, 1 and 2 for the same row. What is happening? Is that correct? No, that is not correct. What should be the correct one? The row should be 0, 1, 2 and column should be 1, 1, 1. So, that is a mistake here. Please correct that. So, this should actually be 0, 1, then this should be what? 1, 1 and this should be 2, 1. I remember at 2 o'clock in the night, I had written it like this and at about 4.30, I thought something was wrong and changed it. So, programming should never be done when you are sleepy. That is a good idea. Anyway, this is a simple and straight forward stuff. Again, the point to be emphasized is that you have to be careful with indices and the root controls that you use. Here is how the diagonal terms are added. So, you have the same diagonal, but one will add 8, 5 and 2, the other will add 6, 5 and 4. So, first one will add 8, 5, 2 and the second one will add 6, 5 and 4. Both the diagonals will get added. There are a large number of such examples. More examples will be given to you as practice examples and I am also trying to put together some notes in which I will give you some solved examples. This probably will be distributed to you next Tuesday and Wednesday. You can perhaps have some material to read also. The point being made is that there are very, very interesting applications of arrays and matrices, both in pure number theoretic issues or graph theory or linked lists and other lists that we will discuss later, as well as several practical problems, one of which we discuss now, which is the handling of digital images. Digital images essentially, so there are points. They may be circular, rectangular, whatever on a picture, any picture, any photograph that you see, it consists of large number of points. In an analog printed picture, it appears as if you have infinite points on all sides, but there is nothing infinite because even the process of making a photograph will ultimately result in certain points. Each one equal to the minimum granularity of the chemical that sticks onto that photo. Each point represents the intensity of light that is reflected to your eyes. That is how you see something as red, something as blue, something as green, whatever. So, effectively you can consider an image to be a collection of picture elements. This picture elements is called a pixel. This is the technical word that is used. A picture element is a value which is representative of the intensity of light at that particular point and such values of intensities together form the total image. So, earlier when we did not have digital computers or digital representation was not required, but when we started looking at representing any analog signals using digital means, we realized that we could digitize a value. So, a value may be on a real line from 0 to infinity. You can say 1, 2, 3, 4. This is how you discretize the points. A picture element, since it represents intensity, people said let us see what is the sensitivity of a human eye to intensity, the maximum intensity that it can recognize, the minimum intensity that it can recognize and let me divide the interval into discrete points. As long as a human being is able to discern just about the difference between two consecutive points, that is good enough. Then I could arrange these points in a two dimensional matrix. Here instead of number of rows and number of columns, I will say the height of the picture is so and so and the width of the picture is so and so. So, what it means in plain terms is that I have a set of h by w values, h number of rows and w number of columns. Each element is a value which is the light intensity. Unfortunately, light is a complex phenomenon. So, we will have to discern between how to represent light intensity depending upon the nature of the light. So, here are three possible representations which are most commonly used. The simplest is of course, the representation of a pure black and white photograph or a diagram. This you encounter whenever you draw lines. For example, the lines that I draw on the screen are drawn in let us say black or whatever color we take that as black. Rest of the background we take that as white. So, the entire picture consists of pixels which are either black or white. Now, since there are only two states, one bit is sufficient to represent every point. Such images are called monochrome images and they have only two values per pixel, zero or one. Zero represents black, one represents white. In real life however, if you take photographs for example, black and white photographs, you see a lot of shades of gray in a black and white photograph. Now, these shades of gray have to be represented by discrete values. Traditionally, the digitization is done using a value range from 0 to 255. So, 0 represents black, 255 represents white. This is because human eye is not capable of distinguishing between gray shades other than the 0 to 255. They are considered more than adequate for human understanding of the human eye. There is another reason why this particular range is chosen. 128 is also actually good enough. In fact, 64 values of digitization are also sometimes sufficient. Zero and one is not sufficient because that will convert everything into black and white. The reason 256 values are chosen is because they can be reprinted by one byte. Remember, one byte is equal to 8 bits and therefore, 8 bits can reprint values between 0 to 255 if you take those values as proper unsigned integer values. How do you represent color images? Color images are said to have every pixel represented by three components. These three components are called red, blue and green or RBG or RGB, whichever way the sequence will use. That means that every color picture point actually is emitting or reflecting three different basic intensities. An intensity of a red light, intensity of a blue light and intensity of a green light. And together, they give you the sense of a color. You can get actually millions of colors. Using the same principle of digitizing every color component separately, I can use 8-bit digitization for red, 8-bit digitization for green, 8-bit digitization for blue and then I will get 24 bits to represent a particular picture value or picture element. So I can have 16 million colors. This is much larger than what a human eye can discern. In any picture, human eye is incapable of distinguishing between more than about 2,000 different colors. That is the known statistical limit of an average human perception, which means that this kind of thing is adequate as far as digitization is concerned. Why are we doing all this amela? This is to ensure that once we can get somehow these values digitized and get them inside the machine, then I can process them any which way I want. And we will see how some very interesting processing can be done on these values to give you better images. First of all, we realize that whenever we deal with a digital image, we are dealing with a very large amount of information. So you take for example, a monochrome image, even a small size image of a finger print will have 500 into 300 bytes. For large images, a modern digital photographer would use a camera which can capture 12 million pixels for example. A 12 million pixels means each pixel is red, blue and green. So 3 bytes at each pixel, one image will be 36 megabytes inside. In spite of having your large desk, if you stuff your desk with let us say 10,000 photographs, you may run out of space. Typically mechanisms are derived in order to represent this information in a more concise form. That form is usually obtained by applying some compression technique to the original data. You take the original data and reduce it in size. Now this reduction can be of two types. One is a reduction which is so mathematically well defined that from the reduced image you can actually always construct the original image without any loss of information. Such a compression is called lossless compression. But more often than not, you will not be able to do that or else you may not get a good compression. So people compromise with quality and use what is known as a lossy compression. A lossy compression is one in which the original image truly can never be reconstructed. But a close approximation can be constructed such that for the human eye, there is not much difference that one can see between the compressed image and the original image. So you might have heard of an image format called JPEG. Most of the popular photographs on the web would be in the form of JPEG files. So these JPEG files are compressed images and they will have pixel values which are represented using some compression formula. I am not going into the details of that. But here I have listed a whole lot of file formats. Again Wikipedia is a rich resource of information about various formats. Even the actual details of those formats, compression techniques, etcetera, etcetera have been described there. Those of you are interested can read that. BMP or the bitmap file is the basic format which is an uncompressed format. So a bitmap file will contain bits representing every pixel explicitly. But JPEG, JPEG, etcetera, etcetera are sort of lossy compression that you use. The point being made here is that you have to use now a notion of a file to represent this data. I mean can you imagine that if you want to process the image, if you want to do something interesting on an image, you write a program, very complex program, let us say 150 lines of code for doing something. And now you say all right, you read the input. How will you input millions of values? And imagine when you are testing your program, you enter 1 million values once to take you a few months I suppose. Then you run the program, find out it does not work. So you again correct the program, rerun it and again spend a month inputting values, it is nonsense. So not only you should be able to automatically read values from files which contain that data, but the data in those files also must be obtained automatically. It will not be possible to write those values by hand. So you do require modern technology in order to do such thing. Fortunately, most of the digital cameras today will capture these images and will be able to give you picture element values in different formats. So in some format they will give you and you will be able to convert them from one format to another. We assume that for our purposes, for our discussion of programming, somehow an image file is available to us which contains the values of various pixels. So once we have these values of digital images, we can read them in a matrix and these matrix elements can be processed further. Assume that we are handling grayscale images. A grayscale image as I mentioned has values between 0 and 255. So 0 is black, 255 is pure white and in between will be the shades of grey. The type and size will be a short int or a care or a unsigned int. In our program we are using int simply. So we are spending four bytes instead of one byte, but that is okay because the memory is more than adequate. After all you are handling one image at a time, that is not a problem. Now we introduce two interesting terms. One is called histogram and the other is called cumulative distribution function. These are terms from statistics. All of you know these terms or no? So histogram is essentially a frequency count. So since there are let us say 500 by 300 images there, each value is between 0 to 255. The count of how many times a particular value occurs? Say pixel value 58. If that occurs 112 times in the original entire image, then 112 becomes the histogram count of pixel value 58 and so on. Since there are 256 pixel values, 0 to 255, there will be 256 such counts. And how do you do the counts? Well, one simple Godagiri method is that you take a pixel value 0 and start looking for pixel value 0 in the two dimensional array. Every time you get a 0, you add one to the count, another count add, add, add. Finally you get 4, 5, 10 pixels. Next you start with pixel value 1 and so on. We shall see an easier technique which uses an important notion called associative arrays. We shall see that in the program that we will write. But essentially it is possible to calculate this histogram. So what do we do with this histogram? Why is histogram important? So let us look at some examples. Here is a sample image. It is enlarged. It shows an 8 pixel by 8 pixel image. Ordinarily a picture element will be very small. Remember the normal pictures will be of size 500 by 800, etcetera, etcetera. So an 8 by 8 image will be a mucher image. You cannot even see it. It will appear as a dot. So this is necessarily expanded for you to see how exactly the different picture elements would behave. Here for example, you see a white spot here. Here you see a blackish spot. You will notice, however, that not a single pixel is a pure jet black pixel. Neither is a pure white pixel here. So this is sort of a fuzzy kind of picture where the extremes of the pixel values are not present here. If you capture the pixel values, these turn out to be the pixel values. This is an 8 by 8 matrix. Just look at this matrix a bit carefully. 52, 55, 61, 66, 70, 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, 100, 90, 40, etc. What is the largest pixel value that you see here? Can you quickly find out? 154. So in fact, if you go to the next image, you will see this as 154. Please note that the background of 154 appears to be pure white. It is not so. When I put a text box to put value 154, the text box appears to be pure white. But in the original image, this is the pixel. What I mean to say is that the highest possible value is 255, which does not exist. Similarly, what is the lowest value? It appears to be around 50, 52. So this zero value is not present here. Now, maybe I can relate, when I see the original diagram again, let us go back to the previous slide. Maybe I can say that if I had picture elements in this picture, which varied from 0 to 255, I will have a sharper image. It is called contrast. The contrast would have been better between picture elements. I will be able to see more clearly. Whereas, if the image pixel points are concentrated in a smaller range of possible pixel values, the contrast is low. One of the things that I would like to do on such images is somehow reassign values to those pixels, keeping the original property of the image intact. But reassign them such that I have a full stretch of the image frequency count histogram from 0 to 255. This particular activity is known as histogram equalization. We shall see that. First of all, let us see what the histogram is. So these are the values. Everything is clear. So this is one sample value, for example. Now, these are the histogram values. So remember what I said about histogram. Histogram is nothing but the count of pixels of a particular value. So there is exactly one pixel with value 52. But if you look at value 55, there are three pixels. This can be confirmed. Let us go back to the pixel table. We are looking at 55. So I have 155 here. Can anybody spot another 55? Yes, it is here. And then down. And no other 55. So there are three 55 values. And therefore, the histogram table says three against 55. Notice that this is not a full histogram table. Wherever there are zero values, they have not been listed. So from 0 to 51, there are pixel values are none in that. There are no pixels at that value. Similarly, at pixel value 53 and 54, there are no pixels. They are not listed here. This then is the histogram table. We notice that the values are between 52 and 154. And that is why the contrast is inadequate. Now, we want to stretch this contrast. It is easy to say a very simple logic, saying that whatever is the minimum value, take it to 0. What is the maximum value? Take it to 255. And in same proportion, move all other values. But in that same proportion, if I move all the values, then the original picture may get distorted. Because there is a certain distribution of pixel values inside that I must give credence to that distribution. And retain somehow the overall distribution wherever I shift them. That is where I require some mathematical formulation. This is obtained by using what is known as a cumulative distribution function. So this slide outlines what we need to do. First, it observes that the pixels move in a very narrow range, whereas the possible values are from 0 to 55. Then, can I stretch the histogram to cover all the possible value so that I will get a better contrast. So to stretch, I need a mapping. I need to, for example, as I said, take the minimum value to 0, maximum value to 255. That is a mapping. So given any pixel value, where it should go to is what I need to determine. And if I can find an appropriate mapping, then I can transform all pixels of a given image by that mapping, get new pixel values and see how that picture looks like. My job is to find out what should such mapping be. And such a mapping is obtained, which is most appropriate for an image through the use of what is known as a cumulative distribution function or CDF. What is a cumulative distribution function? Let us look at that table. A cumulative distribution function is merely the sum total of all pixels which have value up to that or less. So for example, at 52, there was exactly one pixel, you remember. At 55, there were three pixels. But total number of pixels at or below 55 are 3 plus 1, 4. So effectively, I keep adding the individual pixel values to find out the cumulative total. So this, for example, at 79, when I say 48, so at or below 79, there are exactly 48 pixels whose values is either 79 or 78 or 77 or 0 or 1 or 2, whatever. When I calculate the cumulative values, I lose sight of what is the total number of individual pixel values there. But I will get this cumulative. What should be the last entry in this cumulative distribution function? That will obviously be the total number of pixels in the image because all total number of pixels must be accounted for. So 64 pixels exist at or below 154. Now this is where I want to stretch. I want this cumulative distribution function which is sort of a concentrated curve, whether it is normal or how we do not care, but it is a concentrated curve in a small region. I want to stretch that histogram curve. And therefore, the cumulative curve which will move like this in a concentrated region, I want to stretch it into some kind of a straight line from 0 to 255. This is obtained through the formulation called histogram equalization. Again, some of you would be interested in knowing how this formula has come about. So we will not discuss that. Wikipedia will tell you and Wikipedia will give you additional references. But those of you are interested in processing of digital images should certainly read those references. It is absolutely interesting stuff. This is the equalization formula that if there is a value v of a existing pixel in an image, where does it go to? That mapping h of v is given by this. So it is round of cumulative distribution function for that v minus the minimum of cumulative distribution function divided by m into n minus CDF. So what is the minimum value of CDF 1? So I will put that as 1 here. Let us say I want to convert this equalization formula for my monochrome image of size 8 by 8. Since it is 8 by 8, my m equal to n is equal to 8. So this m into n becomes 64. CDF min is 1. This becomes 63. L is the total number of pixel values in the histogram. So 256 values minus 1. And I calculate this. This will turn out to be a fraction, proper fraction. I calculate it to the best accuracy that I can and then I will round it. That will give me the new value for this pixel. So here is an example. The cumulative distribution function for pixel value 78 happens to be 46. So a pixel value 78 will be equalized using this formula. The h 78 goes to round of 46 minus 1 by 63 multiplied by 255. This fraction multiplied by 255. When I round it up, I get 180. What it means in plain English is that if in the original image I had a pixel with value 78, that value will now become 182. But this 182 has not been arbitrarily obtained by some kind of a linear extrapolation or interpolation. It has been obtained keeping in mind the characteristic of the entire image in terms of its histogram and in terms of its cumulative distribution function. Once we know this formula and since there are exactly values possible are between 0 to 255, it is not possible to set up a iteration to calculate where 0 will go, 1 will go, 2 will go, 3 will go, 255 will go. And once I know that, can I not apply that to all my original pixels and say this pixel becomes this, this pixel becomes this, this pixel becomes this. That is how I will get an equalized image. The pixel values after histogram equalization will look like this for this 8 by 8. Observe that the lowest value is 0 and the highest value it was here somewhere 255. And all other values have been appropriately stretched, but they are stretched as per some logic which takes into account the nature of the image. It is not done arbitrarily. This is how the contrast enhanced image will look like. Same image 8 by 8, you apply this formula. Observe that the relative weightages of pixels related to each other have not been disturbed or rather have been disturbed minimally in this stretching. This shows both images side by side. You agree that the second image is better in terms of visibility and clarity. Here is another image. This is a grayscale image of a seed. It is a photograph of some nice meadows. There are some trees and whatever. But again you will observe that this picture suffers from the same lacuna. That is its contrast is very low. Its contrast is very low because this is the histogram. The histogram is all located in the region from somewhere between 120 to 200. All points are between them. And this curve that you see is the cumulative distribution function. So there are very few pixels. There are no pixels in fact below 128. But cumulatively these pixels add up like this and around this point they become maximum. Again there are, there is nothing significant added. What I would like to take, what I would like to do is I would like to see this cumulative distribution function which is this curve to be something like a straight line here. Go from 0 to something and correspondingly stretch the histogram. So this is how I would like an equalized histogram to look like. As a matter of fact if you apply that formula this is exactly what it attempts to do. It stretches the histogram and therefore the cumulative distribution function becomes like this. But it stretches the histogram while retaining its natural shape. That is how the picture is not distorted. Only the contrast is enhanced. If you do that and do the equalization this is the picture you will get. So you agree that this picture is clearer? Now we come to the programming task. We want to start with this picture. So that means assume that picture element values are there in the file. So I should be able to read the entire file. I already know how to read data from file. How do I read it? Input redirection. So I have dot slash a dot out. I will less than picture data something and in that picture data file I will possibly have the value of n, value of n and then m by n pixels in row major order. The standard way in which I would input any other matrix. So I can read that entire. After reading that I will have to calculate the histogram and I will have to calculate the cumulative distribution function. Having calculated that I will have to calculate the mapping function. Where does each point in the original picture element go to the new picture? So having calculated that that equalizer factor equalizing factor is nothing but one which will actually give me this kind of histogram and cumulative. And I apply that equalizer factor. Take a pixel from here convert it using that formula to new pixel. Take another do that I will get this. So programming wise also it does not appear to be a very complex task. I have to calculate histogram. I have to calculate cumulative distribution function. I have to calculate the transformation or mapping function and apply it to get these values. Incidentally if you take some of you at least would have used modern digital cameras and many of you would have used the picture editors, the picture editor software on the machine. You can actually take a picture and click somewhere to get this histogram. And you can actually play with the histogram and as you change the histogram nature the picture changes. What is happening is at every instance you change the histogram nature. All these computations are being done internally and a new picture is being presented. That is now possible because the speed with which the computations can be done inside the machine. In fact many of these algorithms are actually embedded written in CC++ by the way converted into embedded systems into the microprocessors which are inside the digital camera. So on a digital camera itself nowadays you can see a photograph, you can press some button, you can see a contrast, you can actually match the contrast and store the final value. Somebody like you must have written those programs also. So let us look at some programming details now here. First of all how do I calculate entries in the histogram table? As I said there are multiple ways of doing that. First of all I set up an array of 256 elements. 0 will count the number of pixels at 0 pixel 1, 2, 3 etc. etc. Here it shows the final count. How do I get the final count? There are two ways. One is let us say I am looking at 68. So I will examine each pixel in turn. Is there any 68, 68, 68, 68? Ah I find one here. So I increment the count by 1. Initially I start with 0 here. Then I keep looking. Is there another 68? Yes here. So I increase that count. Is there another 68? Yes. Now the count becomes 3. Another 68? Yes. The count becomes 4. Another 68 in here? Yes. Count becomes 5. Another 68? No. That means for every pixel value I allow to scan m by n elements. But there is a better method to solve this issue. The better method is that if I see that I have maximum values between 0 to 255, which also happen to be the indexes of the rows or the elements inside this array. Why not I do one thing? I start with an array. I initialize the entire array to 0 first. Now I do not look at individual pixel values at all. I simply start examining the pixels in my matrix. I take the first pixel. What is its value? 52. Now what I do is I go to the element number 52 in this array somewhere here. That is where I have to add 1 to the count. So effectively it is as if I have 256 counters. All of these counters are set to 0. Now any pixel value I examine, I go to that particular counter and increase that counter by 1. That way when I finish examining all the pixels, all the counters would have been appropriately increased. Using a value in an array as an index of another array is called associativity. I am associating value inside some matrix as an index in some other manner. So the value here 52, 55, 61 is working out to be an index of the corresponding counter here. So which counter will I increment? Suppose this array is let's say histogram array. Then histogram J. What would that J be? That J be the value of that element itself here, the image value. This is how it is done. This is a program which does the histogram equalization. I will put these slides on the models. You can read the comments and so on. This is the basic input stuff. So I calculate the image 500 by 500. I define a two-dimensional matrix for image and new image. And observe that I have three arrays. Histogram, CDF, and equalizer. Equalizer is that mapping function for each picture value that I will need to find out. This is just an input statement. So I will read all the image values. Of course, you can always add validations. Each value should be between 0, 255. Otherwise, you will have a problem. I initialize all table elements to 0 because I want to use these as accumulating sums, all these counter values. Now, observe how the histogram table entries are calculated. An iteration for i equal to 0 to minus 1 and j equal to 0 to n minus 1 cover all pixels. Every time I look at a pixel, whatever is value, image ij, I use it as an index to the array histogram and add 1 to it. This is the simple principle of associative. So basically, I am saying let k equal to image ij and histogram k is added by 1. So I am directly using the value as an index. That is another reason why I must do the validation of input because if any element value is beyond 255, then I will have serious consequences in mind. But the code becomes so much simpler when I use the notion of associative. So this is something I would like all of you to appreciate and understand. It is possible and it is to our great advantage. It simplifies programming. If wherever feasible, I can use the value of something as an index of another array, I just output the histogram values. Now I calculate the cumulative distribution function. The cumulative distribution function is simply addition of all previous histogram elements. So cumulative distribution function of 2 will be sum of 0 and 1 and 2. 3 will be 0, 1, 2, 3, etcetera. So once I calculate, I can cumulatively add these one after another and a simple iteration will get me this. I have to find out the minimum value in the CDF table. Please tell me whether this code will work correctly. I want you to concentrate only on this portion of the code, these four lines. You would have done this 17 times, finding out maximum minimum of an array. I am trying to find out the minimum of an array and asking whether the code will work correctly. Slowly it is dawning on people that there is something wrong. So what is wrong? Whenever I am trying to find out the maximum of all elements, then the initial value for maximum that I said must be lowest lower than the lowest possible value. So if I am looking at positive integers, I will set that maximum to be minus 1. So at the first element itself will be greater than that and subsequently the comparison will be maximum. If I am trying to find the minimum, I must set the initial value of minimum to be higher than the highest possible value in that array. Then only I will get correct minimum. I have done a mistake here. I have set it to 0. So what should it be? Ideally, sorry, 256 or 10000, something which is beyond the possible range. So when you are finding the minimum, you start with the highest possible value and you are finding out the maximum, you start with the lowest value and the lowest and highest should ideally be outside the range of possible value so that you get the correct value. Next I can calculate the entries in the equalization table. Observe that the equalization table entries again use the same formula that we have seen earlier. That formula is from Wikipedia. I am using c d f i minus min divided by m into n minus min multiplied by 256 minus 1 converting into a float this higher quantity before I do the division. Why am I using this float and what is the meaning of this float? Whenever for any value I put in parenthesis a type declarer. Ordinarily I will say in float, double, etc. in my declaration but I can use this name against any value in parenthesis before that value that is known as type casting. So for example if I say float and then say phi then phi will effectively become 5.0 forcibly. That means I am actually converting a value to the desired type it is known as type cast. When I do the type casting the new value of the type in which I am casting that value is what will participate in my expression. Why is that important? Let us look at the details of this particular expression. c d f i is an integer value. Min is an integer value. So the difference will be an integer value. I am dividing this by m into n minus min which is also an integer value. Integer by integer will give you what? An integer. Notice that in the mathematical formulation the example that we had worked out had 0.7382 etc. You will never get that if you divide one integer by another integer. So one of the two either the denominators or the numerators should be forcibly converted into float. There are multiple ways of doing it. You can simply multiply the top thing by 1.0. Simple 1.0 being a float it will take any integer convert into float etc. But this is a more elegant solution. Anyway this is just to emphasize that we could make some very simple silly mistakes of not realizing how the computation will be done for an expression and get into a problem. Round is a built in function which will find out the rounded value. I am just for the sake of it. I am printing equalizer at every pixel value r. What this means again is that if there is a pixel value of 52 it will become this. If there is a pixel value 78 it will become this etc. This is my table. Now this equalizer table is what I want to apply to the original pixels. So whatever was the original value I want to replace that value by the entry into the equalizer table. Suppose there was a pixel value 78 how will I know which value to replace it with? I will go to 78th entry here and whatever the contents are to be put there. Again I am using the notion of an associative value. Whatever is the value of the original pixel I am using it as an index into my array of equalizers and collecting the value and replacing it. So this is the code that you see here. I simply run over all the pixels from 0 to m minus 1 0 to n minus 1. My old image value was image ij. I treat that value as an index into the equalizer. So if image ij was 78 if you recall it was becoming 182 or something. Equalizer 78th element will be that 182 and that 182 will become the new value i and this is how I will get my new image. So is this clear? Now this is just one simple example of an interesting algorithm being applied to enhance the image quality. The whole world of graphics, the whole world of image processing and subsequently the whole world of processing video clips is absolutely amazing and interesting. Those of you who are interested can actually look at a whole lot of algorithms that exist to do this. About two years ago when the Aadhaar project was announced by the government of India I happened to be teaching CS11 and I gave a project to all the students of that class, a single project divided into multiple projects to handle fingerprints. So this is one sample fingerprint. You all know that fingerprints are unique but how do you compare them except for by visual examination? Traditionally visual examination of thumb prints has what been used earlier but you want this to be done automatically. How do you do that? So you take this one fingerprint, you take somebody else's fingerprint and somehow you have to match these fingerprints. Can you match them pixel by pixel? Extremely difficult. There are techniques of doing that but you see I put my thumb like this today and slightly turn it left or right. The individual pixel disposition will be completely different. They will not match. The intensity could be a problem. Maybe I will equalize some. After a whole lot of research in this which has come out from quite a few I would say centuries the modern way of handling digital images is to characterize these images by certain features that you observe on these images. Notice for example there are words like this. There is a branch suddenly somewhere here. This is called an island. There are ridges. These special points in any fingerprint are called minutiae points and they are characterized by the slopes of the curves around them and so on. It's a very very interesting what should I say science in itself and there are a whole lot of algorithms which are available to characterize these images. They convert these images into a set of feature points. Since images are unique these feature points also should turn out to be unique. So either they are positions or they are relative values etc etc and there is a whole lot of software that is available. There is a very large group of open source software for fingerprint analysis available through a national institute in the United States. They develop this fingerprint analysis software and they release it to the whole world to be freely used. Many countries use that as the base implementation for many of their problems. Here I would just like to mention to you the kind of size that you have to deal with. Imagine that you have a bank account and you get an Aadhaar number and you are asked to put your fingerprint there and that has to be ensured that it is not somebody else's fingerprint but your own fingerprint. Imagine that about 120 crore Indians fingerprints have been captured somewhere. Can you imagine that you put your fingerprint it gets compared with 120 crore fingerprints somewhere else. Why you will wait for eternity till you get your 100 rupees or 500 rupees from your ATM. That is not possible. You have to have mechanisms, you have to have algorithms and you have to have entire end to end system which is so designed and so implemented that you can at least find whether you are you are not very quickly. Whether you have to find duplicates that is a larger problem. But you can see the kind of problem that the nation is grappling with. No other country in the world has ever attempted to give a unique identification based on this kind of biometrics. We are doing that. China will probably be following. No other nation has so many people. So that is an issue that we are tackling. Thank you so much.