Tutorial on spToolkit
Transcription
Tutorial on spToolkit
Tutorial on spToolkit A.C. Klaren August, 2004 2 Chapter 1 Introduction 1.1 The SmartSpectra project The SmartSpectra project started in 2002 with the objective to design and develop a multispectral sensor with a set of features that permits its use in commercial applications. It will be provided with a set of high-level software tools to simplify its application as a developer’s toolkit for any possible field of application where multispectral imaging is needed. This set of high-level software tools has been written in C++ and can be found in a library called spToolkit. 1.2 The spToolkit library The spToolkit library contains a number of high-level software tools to facilitate the process of extracting information from the images obtained by the multispectral camera. All its functions can be applied to images with any number of bands and to images of various data types. This tutorial will show you how to create a program in Microsoft Visual C++ 6.0, which will segment an image of an orange, using multispectral techniques provided by the spToolkit library. 3 4 CHAPTER 1. INTRODUCTION Chapter 2 Tutorial 2.1 Creating a Windows program In order to visualize the results, this tutorial explains the use of the spToolkit in a Windows environment. The Microsoft platform was chosen due to the fact that most partners in the SmartSpectra project are using this platform. This section shows how to create an application in Microsoft Visual C++ 6.0 that creates windows to show images stored in memory in the spToolkit format. 1. Start Microsoft Visual C++ 6.0 2. Click File, and then New 3. Under the tab Projects, choose MFC AppWizard (exe) 4. Type Tutorial as the project name, choose a location (optional) and click OK 5. A window with the title MFC AppWizard opens. Choose Dialog based 6. Click Finish 7. The window ”New Project Information” opens. Click OK The result is shown in Figure 2.1 Now that you have created the project, it’s time to adjust it to your needs. Our objective is to write a program that segments a multispectral image of an orange. To make things easy, we will integrate the possibility to view images in a window. The window that you have already created will be the control window. We will create an other window to show the images. 1. Click with the right mouse button on OK in the Tutorial window 2. Choose Properties 3. Change the Caption from OK to Start 4. Change the ID from IDOK to IDSTART 5. Close the Properties window by click the ”X” on the top right of the window 5 6 CHAPTER 2. TUTORIAL Figure 2.1: A screenshot of the new project. 6. Find the folder Dialog in the Resource View on the left and click on it with the right mouse button 7. Choose Insert Dialog 8. Click on the OK button in the new Dialog window and press delete 9. Click on the Cancel button and press delete 10. Save the project Your screen should now look like Figure 2.2. To be able to use this window, you need to add a class to this dialog window and arrange the destruction. 1. Click with the right mouse button on the Dialog window 2. Choose ClassWizard. The window ”Adding a class ” opens 3. Make sure that ”Create a new class” is selected and click OK 4. Write CImWin as class name 5. Choose CDialog as base class 6. Click OK 7. Look in the list ”Messages” for PostNcDestroy and click Add Function 8. Click Edit Code 9. write the following code in this function: delete(this); 2.1. CREATING A WINDOWS PROGRAM Figure 2.2: Created a second window to show the images. Figure 2.3: The OnNcDestroy function. 7 8 CHAPTER 2. TUTORIAL 10. Save the project Check if you screen looks like Figure 2.3. Now that you designed a dialog window, added a class and arranged the destruction, it’s time to actually start writing our own code. To be able to use the toolkit, we will have to insert come includes and libaries, etc. 1. Open Windows Explorer and go to the folder /spToolkit files which is part of this tutorial 2. copy spToolkit.dll and 1394camera.dll to your project folder (/Tutorial) 3. Copy the subfolders /include and /lib to your project folder 4. Go back to Microsoft Visual C++ 5. Choose Project in the menu bar and next Add To Project, and then Files... 6. Double click the folder lib 7. Change Files of Type to All Files 8. Select both files and click OK 9. Choose Project again, and Settings. A window called, Project Settings, opens 10. Choose the C/C++ tab 11. Change the Category to Preprocessor 12. Write ./include at the Additional Include Directories option 13. Choose the Link tab 14. Change the Category to Input 15. Write ./lib at the Additional Library Directories option 16. Click OK The Project Settings Window is shown in Figure 2.4. Now continue with inserting the includes and namespaces. 1. Click the tab FileView on the left side of the screen 2. Double click the file Tutorial.h 3. Insert the following codes at the 10th line: #include ”spToolkit.h” and using namespace sp; 4. Click File in the menu bar and next Save All 5. Click Build in the menu bar and next Build Tutorial.exe If the compiling went alright, you’re screen should look like Figure 2.5. Now you are ready to use toolkit commands. The next step is to write the code for the CImWin class. The idea is that this class will show RGB images in its window. First we will modify the standard constructor so that it can receive toolkit image data. 2.1. CREATING A WINDOWS PROGRAM Figure 2.4: The Project Settings Window. Figure 2.5: A successfully compiled project. 9 10 CHAPTER 2. TUTORIAL 1. Click on the FileView tab on the left and double click the file ImWin.h 2. Look for the standard constructor, and add a parameter so that it will look this: CImWin(rgb image cl image, CWnd* pParent = NULL); 3. Now go to the ImWin.cpp file in the same way and add the same parameter to the standard constructor 4. Now insert the code below this box in this constructor row=image.GetRows(); col=image.GetCols(); bts = new BYTE[row*col*4]; unsigned int i=0; for (unsigned int r=0; r < row; r++) for (unsigned int c=0; c < col; c++) { for (unsigned int b=0; b < 3; b++) { // 2-b because windows images are BGR bts[i] = image(r,c,2-b); i++; } i++; } Create(IDD_DIALOG1,pParent); // The size of the window is bigger than the image due to its borders MoveWindow(0, 0, col+6, row+25, false); ShowWindow(SW_SHOW); 5. Go to the function ”void CImWin::PostNcDestroy()” and insert ”delete[] bts;” before ”delete this” 6. Save all Your screen should look something like Figure 2.6. The constructor has been written, now it is time to write the actual Painting function which is responsible for drawing the image on the screen. 1. Click on the ResourceView on the left and double click IDD DIALOG1 2. Click with the right mouse button on the Dialog window and choose ClassWizard 3. Look in the ”Messages” list for WM PAINT 4. Click Add Function 5. click Edit Code 6. Now insert the following code in the function OnPaint CDC bmDC; // Create compatible device context bmDC.CreateCompatibleDC(&dc); HBITMAP hBmp; // Create bitmap hBmp=CreateCompatibleBitmap (dc, col, row); SetBitmapBits(hBmp,col*row*4,bts); 2.1. CREATING A WINDOWS PROGRAM 11 Figure 2.6: The modified constructor. bmDC.SelectObject(hBmp); //Select bitmap into dc.BitBlt(0,0,col,row,&bmDC,0,0,SRCCOPY); compatible dc and show 7. Save all Your screen should now look like Figure 2.7. The only thing that is still missing, is the declaration of several variables in the header on this class. 1. Click on the tab ClassView in the left of the screen . 2. Find CImWin in the pull down list and click it with the right mouse button 3. Choose Add Member Variable. The window Add Member Variable opens 4. Choose in the Variable type box: unsigned int 5. Type in the Variable name box: row 6. Choose Private 7. Click OK 8. Now add unsigned int col in the same way 9. And add BYTE* bts 10. Save all 12 CHAPTER 2. TUTORIAL Figure 2.7: The OnPaint function. Figure 2.8: The Add Member Variable window. 2.1. CREATING A WINDOWS PROGRAM 13 Figure 2.9: Including ImWin. The Add Member Variable window is shown in Figure 2.8. Now you have finished writing this class. When you call the constructor of an object of this class, passing on an rgb image, it will automatically create a window and show the image. We will see that now. 1. Click the tab Resource View on the left side of the screen 2. Find the dialog IDD TUTORIAL DIALOG and double-click it 3. Now double-click the Start button in the Tutorial window. A window called ”Add Member Function” opens. Click OK 4. A new function has been created in the TutorialDlg.cpp file, called OnStart. This function will be called whenever the user clicks on Start 5. Insert the following code: rgb_image_cl rgb_im(200,200); rgb_im.Clear(150); CImWin *iw = new CImWin(rgb_im, this); 6. Scroll to the top of this file and type at the line 7: #include ”ImWin.h” 7. Save all 8. Compile project If your project compiled successfully, your screen should look like Figure 2.9. Now it’s time to run your program! Before running it, imagine what you expect 14 CHAPTER 2. TUTORIAL Figure 2.10: The result! to see. Press F5 to Run. Figure 2.10 is what I see on my screen, after pressing Start! 2.2 Load and save multispectral images This section will show you how you can load images, cast them from one type to another, extract bands and save them. We will start with loading a PPM image into an image class of unsigned chars. 1. Remove the following lines from the function OnBnClickedOk: rgb_image_cl rgb_im(200,200); rgb_im.Clear(150); CImWin *iw = new CImWin(rgb_im, this); 2. Replace them by this code: ubyte_image_cl ub_im; input_file_cl inp_file("oranges", PPM); inp_file >> ub_im; CImWin *iw = new CImWin( (rgb_image_cl)ub_im, this); 3. Save all 4. Press F5 to run the project 5. Click the Start button on your project 2.2. LOAD AND SAVE MULTISPECTRAL IMAGES 15 Figure 2.11: Showing oranges on the screen. You will now see a photo of an orange tree in your screen, see Figure 2.11. When we study the few lines of code we just wrote, we’ll see that the casting function is situated in the last line, casting a ubyte image cl to an rgb image cl. Let’s expand the number of bands from 3 to 33. We’ll load an ENVI image into the program’s memory and we’ll choose 3 bands, and show them as an RGB image on the screen. 1. Remove the 4 lines of code that you entered before and replace them with: ubyte_image_cl ub_im; input_file_cl inp_file("./images/orange_multiband_ubyte", ENVI); inp_file >> ub_im; rgb_image_cl rgb_im(ub_im.GetRows(), ub_im.GetCols()); rgb_im.PutBand(ub_im[15],0); rgb_im.PutBand(ub_im[16],1); rgb_im.PutBand(ub_im[17],2); output_file_cl outp_file("./images/output_orange_ubyte", PPM); outp_file << rgb_im; CImWin *iw = new CImWin(rgb_im, this); 2. Save all 3. Press F5 to run the project This little program loaded an ENVI image in its memory, placed 3 of its bands into an RGB image, writes it to a file and to the screen (see Figure 2.12). 16 CHAPTER 2. TUTORIAL Figure 2.12: Three bands taken from an ENVI image and shown as an RGB image. You have probably been slightly annoyed by the fact that this program took quite a long time to be executed. This is mainly due to the fact that the ENVI file is 11 MB big. A solution to the program is to load just the 3 bands that you really need, instead of all of them. This can be done in the following way. 1. Replace the code: ubyte_image_cl ub_im; input_file_cl inp_file("./images/orange_multiband_ubyte", ENVI); inp_file >> ub_im; rgb_image_cl rgb_im(ub_im.GetRows(), ub_im.GetCols()); rgb_im.PutBand(ub_im[15],0); rgb_im.PutBand(ub_im[16],1); rgb_im.PutBand(ub_im[17],2); 2. ...with this code: rgb_image_cl rgb_im; input_file_cl inp_file("./images/orange_multiband_ubyte", ENVI); inp_file.LoadImage(rgb_im,15,17); 3. Save all 4. Press F5 to run the project As you will have noticed, the program executes much quicker now. However, LoadImage can only load a number of successive bands. 2.3. ERROR HANDLING 17 Figure 2.13: An unclear error message. 2.3 Error handling In this section we will explore the different ways of error detection. 1. Replace the code that you typed in the function OnBnClickedOk with the following code: rgb_image_cl rgb_im; input_file_cl inp_file("./images/bird",PGM); inp_file >> rgb_im; rgb_im(600,125,1)=255; CImWin *iw = new CImWin(rgb_im, this); 2. Save all 3. Press F5 to run the project You should have gotten an error message as shown in Figure 2.13. Normally, at this moment you would start a long process of searching for the error, debugging, etc. Fortunately, the toolkit has a tool incorporated to look up errors quickly. It is based on the C++ method ”try-throw-catch”: you try a function, an error might be thrown which will be caught and handled. Here is how it works. 1. Add the try and catch function as shown here: try { rgb_image_cl rgb_im; input_file_cl inp_file("./images/bird",PGM); inp_file >> rgb_im; rgb_im(600,125,1)=255; CImWin *iw = new CImWin(rgb_im, this); } catch (error_message_cl e) {MessageBox(e.ReturnReport().c_str());} 2. Save all 3. Press F5 to run the project Obviously you still got an error, but it is not so unclear as it was before. Now you’re being told that there is something wrong with the number of bands 18 CHAPTER 2. TUTORIAL Figure 2.14: Still an error message, but with a hint. of the image. Let’s have a closer look at the program. You have an RGB image, which obviously has three bands. Then, you try to save a PGM image in it, which has just one band. The toolkit doesn’t know what to do with the two remaining bands and throws an error of type error message cl. Let’s adjust the program to eliminate the error. 1. Rewrite the program as follows: try { ubyte_image_cl ub_im; //ubyte_image_cl can have any number of bands input_file_cl inp_file("./images/bird",PGM); inp_file >> ub_im; ub_im.AddBands(2); //expand the image with 2 bands to a total of 3 ub_im.PutBand(ub_im[0],1); //copy the first band into the second ub_im.PutBand(ub_im[0],2); //copy the first band into the third ub_im(600,125,1)=255; //change the pixel CImWin *iw = new CImWin( (rgb_image_cl)ub_im, this); //don’t forget casting to rgb_image_cl! } catch (error_message_cl e) {MessageBox(e.ReturnReport().c_str());} 2. Save all 3. Press F5 to run the project A new error occurred, saying that the requested row number is higher than the available number of rows. The problem appears to be related to the part of de code where we assign a new value to the pixel at row=600, column=125 and band=1. The image does not have 600 rows, which explains the error. 1. Change this line of code: ub_im(600,125,1)=255; 2. into this: ub_im(60,125,1)=255; 3. Save all 4. Press F5 to run the project If all went well, you did not get any errors and the bird has a little dot in his eye (see Figure 2.16). The moral of the story: don’t forget to use try/catch when you use toolkit instructions! 2.3. ERROR HANDLING 19 Figure 2.15: Another error. Figure 2.16: The result. 20 CHAPTER 2. TUTORIAL Figure 2.17: The cropped image. 2.4 Performing image operations Now it is time to actually do something with our images! As said before, the goal of this tutorial is to perform a segmentation on a multispectral image of an orange. To avoid a long processing time, we’ll crop the image, concentrating on the part that really matters. 1. Replace the code that you typed in the function OnBnClickedOk with the following code: ubyte_image_cl ub_im; input_file_cl inp_file("./images/orange_multiband_ubyte",ENVI); inp_file >> ub_im; ub_im.Crop(100,200,400,500); CImWin *iw = new CImWin( (rgb_image_cl)ub_im, this); //casting to RGB results in showing only the first 3 bands 2. Save all 3. Press F5 to run the project The image has become smaller, only showing the most interesting part. See Figure 2.17. We will now threshold the image. Since it is a multispectral image, we need to give a threshold value for every band. This is done by means of a vector. To keep things simple, we give the same value for every band. 1. Insert the following code after the line of code where you crop the image. vector<unsigned char> v(ub_im.GetBands(), 50); ub_im = ub_im.Thresholding(v); // Copy the first band into the second and third // to create a gray RGB image. ub_im.AddBands(2); ub_im.PutBand(ub_im[0],1); ub_im.PutBand(ub_im[0],2); 2. Save all 2.4. PERFORMING IMAGE OPERATIONS 21 Figure 2.18: The segmented image. 3. Press F5 to run the project The result is shown in Figure 2.18. This simple thresholding function did a pretty good job segmenting the defects of the orange. By adjusting the thresholding values for every band, the results can easily improve. You can try that yourself. Now we will detect the borders of the segmented image, using convolution. We will use a convolution kernel for horizontal edge detection and one for vertical edge detection. Then, we will combine those results into one image. 1. Insert the following code after the line of code where you threshold the image. //create both kernels and perform the convolution gray_ubyte_image_cl gub_im_x, gub_im_y; mask_cl mask1(Sobel_x); gub_im_x=ub_im.Convolution(mask1); mask_cl mask2(Sobel_y); gub_im_y=ub_im.Convolution(mask2); // Create a real image and use it to write the results to gray_real_image_cl gr_im(ub_im.GetRows(), ub_im.GetCols()); for (unsigned int r=0; r<gr_im.GetRows(); r++) for (unsigned int c=0; c<gr_im.GetCols(); c++) gr_im(r,c)=sqrt(pow(gub_im_x(r,c),2)+pow(gub_im_y(r,c),2)); // Normalize the image and cast it to ubyte_image_cl gr_im=gr_im.Normalization(0,255); ub_im=(ubyte_image_cl) gr_im; 2. Save all 3. Press F5 to run the project The result (shown in Figure 2.19) is not very impressive. This is due to the amount of noise present in the thresholded image. There are various techniques 22 CHAPTER 2. TUTORIAL Figure 2.19: Edge detection. to improve the result, like e.g. region growing. However, this is not further discussed in this tutorial and neither is it available in the toolkit (yet). 2.5 Band selection Working with multispectral bands is usually very time consuming, especially when the image has a large number of bands. Try the following program and clock the time it takes to execute (It could take up to half an hour). 1. Replace the code that you typed in the function OnBnClickedOk with the following code: // Load the source image ushort_image_cl us_im; input_file_cl inp("./images/sp_orange_20_b_vis", ENVI); inp >> us_im; // Transform the image from 12 bit to 8 bit ubyte_image_cl ub_im; ub_im = (ubyte_image_cl) (us_im/16); // Only use the part of the image that shows the orange ub_im.Crop(100,150,400,550); // Scale the image to reduce the classification time ub_im=ub_im.Scale(0.4, 0.4); // Load the trainingset ubyte_image_cl training; input_file_cl training_file("./images/training", ENVI); training_file >> training; // Perform the 1-NN classification gray_ubyte_image_cl label(ub_im.GetRows(), ub_im.GetCols()); label.Clear(); double d; 2.5. BAND SELECTION 23 for (unsigned int r=0; r<ub_im.GetRows(); r++) for (unsigned int c=0; c<ub_im.GetCols(); c++) { double len=1000; // start with a great length // Determine the distance from the pixel to every prototype for (unsigned int rr=0; rr<training.GetRows(); rr++) for (unsigned int cc=0; cc<training.GetCols(); cc++){ d = dist(ub_im.GetPixel(r,c),training.GetPixel(rr,cc)); if (d < len) { // give this pixel the label of the closest neighbour label(r,c)=training(rr,cc,training.GetBands()-1); len = d; } } } // Save the label file output_file_cl out("label\_all\_bands", PGM); out << label; // Copy the first band into the second and third to create a gray RGB image. ub_im = label; ub_im.AddBands(2); ub_im.PutBand(ub_im[0],1); ub_im.PutBand(ub_im[0],2); CImWin *iw = new CImWin( (rgb_image_cl)ub_im, this); 2. Now include the following function in you project. Place this function above the function OnBnClickedOk. double dist(ubyte_pixel_tp c1, ubyte_pixel_tp c2) { //Determine the distance between c1 and c2 double sum=0; for (unsigned int b=0; b<c1.GetBands(); b++) sum += pow(c2[b]-c1[b],2); return sqrt(sum); } 3. Save all 4. Press F5 to run the project As you have probably guessed, this program is a 1-NN classifier. I determines the distance between a multispectral pixel of the source image and every prototype in the trainingset. Next, it labels this pixel with the same class as the nearest prototype belongs to. The result is shown in Figure 2.20. Since our image has 33 bands, this is a very costly process. Especially if you consider that you might need to repeat this process all the time, which would be the case with classifying oranges in a warehouse! It would safe a lot of time if you could reach a similar result with just a part of those bands. This is what the band selection function does; it selects the bands that together contain a large amount of information and simultaneously are the least mutually correlated. The band selection process is time-consuming, but consider that it only has to be performed once to know which bands are most 24 CHAPTER 2. TUTORIAL Figure 2.20: The result of the classification process (black=background; gray=orange; white=overripe). useful for the type of classification that you are interested in. Clock the time again and compare the result with the previous result. 1. Add the following code behind the part where you transformed the image ub im from 12 bit to 8 bit: // Perform band selection ub_im = ub_im.BandSelection(3); 2. Add the following code behind the part where you load the training file into the image training: // Pick the previously selected bands from the trainingset ubyte_image_cl training_temp(training.GetRows(),training.GetCols(),4); vector<unsigned int> sb; sb = ub_im.GetSelectedBands(); training_temp.PutBand(training[sb[0]],0); training_temp.PutBand(training[sb[1]],1); training_temp.PutBand(training[sb[2]],2); training_temp.PutBand(training[training.GetBands()-1],3); training = training_temp; 3. Change the output file name from label all bands to label sel bands. 4. Save all 5. Press F5 to run the project As you can see, the result is very similar to the previous result. The band selection function is unsupervised and can therefore be used in any situation. There is another reason that explains the slowness of this program. The images are stored in memory according to the Band Sequential format (BSQ). That means that bands are stored one after another, in contrast to Band Interleaved by Pixel (BIP). BIP means that multispectral pixels are stored one after the other. In the classification program shown above, multispectral pixels are repeatedly asked for. Due to the BSQ format used in the toolkit, these pixels have to be built every time. Obviously, this is very time-consuming. A solution would be to create the pixels one time and store them for further use. 2.6. USING A FIREWIRE CAMERA 25 Figure 2.21: The result of the classification process using band selection (black=background; gray=orange; white=overripe). 2.6 Using a Firewire camera The toolkit is designed to work with Firewire cameras with D-cam specification, including the SmartSpectra camera system. In this version of the toolkit the interface to the SmartSpectra camera system is not activated yet, but the general interface to Firewire cameras is. In this section we will find out how to use the camera, how to communicate with it and how to take photos. Before you start this section, you should be sure that the camera driver has been properly installed and the camera is plugged in. 1. Place the following code into the function OnBnClickedOk: ubyte_image_cl ub_im; camera_cl cam; cam >> ub_im; // // // // if { The camera constructor determines automatically which video mode your camera can handle. Therefore, it is not sure if the result will contain 1 band (gray) or 3 bands (color) (ub_im.GetBands() < 3) ub_im.AddBands(2); ub_im.PutBand(ub_im[0],1); ub_im.PutBand(ub_im[0],2); } CImWin *iw = new CImWin( (rgb_image_cl)ub_im, this); 2. Save all 3. Press F5 to run the project With some cameras you’ll get a black or gray image. This could be caused by the fact that they need a ”warming up” period. Later on in this section a method will be introduced that captures several images in a row. This could 26 CHAPTER 2. TUTORIAL solve the problem, because it allows you throw away the first couple of images and use the last one. You can find out in which video mode your camera is running. 1. Add the following code behind the part where you read the image from the camera into ub im: stringstream ss; ss << "Format: " << cam.GetVideoFormat() << endl << "Mode: " << cam.GetVideoMode() << endl << "Rate: " << cam.GetVideoFrameRate(); MessageBox(ss.str().c_str()); 2. Save all 3. Press F5 to run the project A very important part of the camera cl class are the ControlRegisters. These registers handle the different features of the camera, like the shutter, gain, sharpness, etc. These registers are of type C1394CameraControl, which has the following functions: Construction C1394CameraControl() virtual C1394CameraControl() Values void GetValues() // gets current values from camera void SetValues() // sets values Modes void TurnOn(bool on) // turns a feature on (true) or off (false) void SetOnePush() // sets the one-push auto-mode command void SetAutoMode(bool on) // sets the auto-mode (true) or manual-mode (false) Status void Inquire() // updates feature availability void Status() // updates current values Image that you would like to know if you can adjust the shutter speed of your camera. You would like to know if this control function is present in your camera, if it can be turned on and off and what its minimum and maximum allowed values are. 1. Add the following code behind the last part you added: // You need to call the function Inquire() to update the values cam.ControlShutter().Inquire(); stringstream ss2; ss2 << "Present: " << cam.ControlShutter().m_present << endl << "On/off: " << cam.ControlShutter().m_onoff << endl 2.6. USING A FIREWIRE CAMERA 27 << "Value_min: " << cam.ControlShutter().m_min << endl << "Value_max: " << cam.ControlShutter().m_max << endl; MessageBox(ss2.str().c_str()); 2. Save all 3. Press F5 to run the project Now let’s find out what the current value is and change it. 1. Add the following code behind the last part you added: // You can update by using Status() or GetValues() cam.ControlShutter().Status(); int old_value, new_value; old_value = cam.ControlShutter().m_value1; cam.ControlShutter().m_value1 = 200; cam.ControlShutter().SetValues(); new_value = cam.ControlShutter().m_value1; stringstream ss3; ss3 << "Old value: " << old_value << endl << "New value: " << new_value; MessageBox(ss3.str().c_str()); 2. Save all 3. Press F5 to run the project Luckily, writing values to these registers can be done in an easier way: 1. Replace the following code: cam.ControlShutter().m_value1 = 200; cam.ControlShutter().SetValues(); 2. by this code: cam.SetShutter(200); 3. Save all 4. Press F5 to run the project As mentioned before, there is another capturing method. This method captures a certain number of images in a row at high speed. They are all saved in one multispectral image structure. 1. Now replace all the code you wrote in this section by: ubyte_image_cl ub_im; camera_cl cam; cam.Acquisition(10,ub_im); //Move the last three bands to the first bands //They will be shown on the screen ub_im.PutBand(ub_im[ub_im.GetBands()-1],2); ub_im.PutBand(ub_im[ub_im.GetBands()-2],1); ub_im.PutBand(ub_im[ub_im.GetBands()-3],0); CImWin *iw = new CImWin( (rgb_image_cl)ub_im, this); 28 CHAPTER 2. TUTORIAL 2. Save all 3. Press F5 to run the project If the resulting image is to dark or to bright, you might want to adjust the shutter. Practically all the functions available in the camera driver are also available in the toolkit. This is because there are situations in which it is more convenient to use those than the higher level functions of the toolkit. However, this subject is not being dealt with in this tutorial. 2.7 Other functions There are still a number of functions that have not been treated in this tutorial. In this section we will write one program that shows several functions at the same time. First we’ll introduce the Region Of Interest (ROI). It is one of the constructors of the image classes, which allows you to construct a copy of another image (or part of another image). The difference with the copy constructor is that the ROI image doesn’t have its own memory. It actually points to the memory of the source image. As a consequence, if you change the appearance of a ROI image, its source image will change as well. 1. Place the following code into the function OnBnClickedOk: ubyte_image_cl ub_im; input_file_cl inp_file("./images/oranges",PPM); inp_file >> ub_im; // create a region of interest (ROI) ubyte_image_cl roi(ub_im,10,70,80,80); // rotate the region of interest roi=roi.Rotate(90); // Show the region of interest CImWin *iw1 = new CImWin( (rgb_image_cl)roi, this); // Notice that the source image changes as well CImWin *iw2 = new CImWin( (rgb_image_cl)ub_im, this); 2. Save all 3. Press F5 to run the project With the toolkit you can calculate the Shannon Entropy of any multispectral image. The entropy is a measure for the amount of information that an image contains. 1. Extend your program with the following code: // Calculating the entropy of this image (measure of information) stringstream ss; ss << "The Shannon Entropy of ub_im is: " << ub_im.ShannonEntropy(256); MessageBox(ss.str().c_str()); 2.7. OTHER FUNCTIONS 29 Figure 2.22: The ROI image. Figure 2.23: The source image that was used to create the ROI. 2. Save all 3. Press F5 to run the project It takes about a minute to calculate the entropy. As a absolute number the entropy doesn’t mean much, but you can use it to compare the information of one image with another. Let’s threshold the image and calculate the entropy again. Do you expect it to be higher or lower? 1. Extend your program with the following code: // threshold the image and show it on the screen vector<unsigned char> v(ub_im.GetBands(), 50); ub_im = ub_im.Thresholding(v); // change the 1 band label image into a 3 band image ub_im.AddBands(2); ub_im.PutBand(ub_im[0],1); ub_im.PutBand(ub_im[0],2); CImWin *iw3 = new CImWin( (rgb_image_cl)ub_im, this); // Now calculate the entropy again. What do you expect to see? stringstream ss2; ss2 << "The Shannon Entropy of the thresholded ub_im is: " << ub_im.ShannonEntropy MessageBox(ss2.str().c_str()); 2. Save all 30 CHAPTER 2. TUTORIAL 3. Press F5 to run the project 2.8 epilogue You have reached the end of the tutorial. You have been introduced to a number of basic operations to give you a basic understanding of how the toolkit works. However, more functions will be added to the toolkit and consequently, the tutorial will be updated too. If you have any questions of comments, you can send them to [email protected]. Good luck with your multispectral image processing!