Просмотр исходного кода

Added explanation of sample code and screenshots to tutorial 2

Clemens-Alexander Brust 11 лет назад
Родитель
Сommit
1e3449d3b6

+ 106 - 6
core/tutorial/02_grayscale.md

@@ -39,15 +39,115 @@ To calculate an image's histogram, use the constructor:
 NICE::Histogram histogram(image, 0, 256);
 ```
 
-### Equalization
-The sample code uses the pattern from the beginning and the _Histogram_ class to
-optimize the source image's contrast.
+# Sample code
+This sample program attempts to equalize an image's histogram by deriving a
+gray value transform from the cumulative histogram.
 
-The transformation works by computing the cumulative histogram like this:
+Take this ![sample image](screenshots/tut02_eq_org.png):
+the ![histogram](screenshots/tut02_eq_hist_org.png) shows that it doesn't make
+use of the whole range of gray values.
+After running the sample program, the image should look like
+![this](screenshot/tut02_eq_new.png).
+We can see in its ![histogram](screenshot/tut02_eq_hist_new.png) that is uses
+the complete range now.
+
+## Includes and file names
+Just like the previous sample, the program needs input and output file names.
+It also uses the _ImageT_ and _ImageFile_ classes, but needs the relevant
+headers for _Histogram_ and _VectorT_ as well.
+
+```c++
+#include <iostream>
+#include <string>
+#include <core/image/ImageT.h>
+#include <core/image/ImageFile.h>
+#include <core/image/Histogram.h>
+#include <core/vector/VectorT.h>
+```
+
+The command line is then checked and stored like in the first sample. Most
+sample programs will use this pattern.
 
 ```c++
+// Check if enough parameters were supplied
+if (argc < 3) {
+	std::cout << "USAGE: " << argv[0] << " <input image> <output image>\n";
+	return -1;
+}
+
+// These are our file names
+std::string input_path(argv[1]);
+std::string output_path(argv[2]);
+```
+
+## Calculating the histogram
+We read the image file specified as a command line argument using _ImageFile_
+like we learned in the first tutorial. This time we don't use _ColorImage_,
+because the sample program describes a gray value transform.
+
+When constructing a _Histogram_, we need to specify the range of gray values
+in our image. 8-bit images have values ranging from 0 to 255, but while the
+histogram includes the lower boundary as a bin, it does not include the upper
+boundary because it uses the difference between the boundarys to calculate the
+number of bins. This is why the sample program uses the values 0 and 256.
+
+The cumulative functions doesn't return another _Histogram_, but an _IntVector_.
+
+```c++
+// Read image into memory
+NICE::ImageFile source_file(input_path);
+NICE::Image image;
+source_file.reader(&image);
+
+// Calculate cumulative histogram
+NICE::Histogram histogram(image, 0, 256);
 NICE::IntVector* cumulative_histogram = histogram.cumulative();
 ```
 
-It is then scaled down to make it a homogenuos transform from [0,255] to [0,255].
-The histogram of the resulting image *should* resemble a uniform distribution.
+## The transform
+In theory, if we use the cumulative histogram as a mapping for our gray value
+transform, the resulting image has a perfectly uniform histogram. We can prove
+this for images that are continuously differentiable functions of real numbers.
+This is not the case for digital images:
+suppose an image is filled with a single color.
+There is no gray value transform that can turn this image into one with a
+uniform distribution of gray values.
+
+We also need to scale the cumulative histogram down to our 8-bit range.
+The largest value will match the total number of pixels in the source image,
+so we use that to calculate a scaling factor.
+
+```c++
+double factor = 255.0/(double)(image.width()*image.height());
+
+// Transform using our pattern
+for(int x = 0; x < image.width(); x++) {
+	for(int y=0; y < image.height(); y++) {
+		// This is the old gray value
+		Ipp8u pixel = image.getPixelQuick(x, y);
+
+		// We use it as and index into the cumulative histogram to
+		// computer the new one. This has to be scaled appropriately
+		// using our precomputed factor.
+		double new_pixel_f =
+				((double)cumulative_histogram->get(pixel))*factor;
+
+		// The pixel format is 8-bit integer, so we have to convert
+		// the result
+		Ipp8u new_pixel = static_cast<Ipp8u>(new_pixel_f + 0.5);
+
+		// ..and save it
+		image.setPixelQuick(x, y, new_pixel);
+
+	}
+}
+```
+
+We can now write the changed image back to disk. We use a new file to preserve
+the original.
+
+```c++
+NICE::ImageFile dest_image(output_path);
+dest_image.writer(&image);
+return 0;
+```

+ 7 - 6
core/tutorial/progs/02_grayscale.cpp

@@ -11,7 +11,9 @@
 #include <iostream>
 #include <string>
 #include <core/image/ImageT.h>
+#include <core/image/ImageFile.h>
 #include <core/image/Histogram.h>
+#include <core/vector/VectorT.h>
 
 /*
  * Entry point
@@ -28,10 +30,8 @@ int main(int argc, char** argv) {
 	std::string input_path(argv[1]);
 	std::string output_path(argv[2]);
 
-	// Read file header and display header information
-	NICE::ImageFile source_file(input_path);
-
 	// Read image into memory
+	NICE::ImageFile source_file(input_path);
 	NICE::Image image;
 	source_file.reader(&image);
 
@@ -40,7 +40,7 @@ int main(int argc, char** argv) {
 	NICE::IntVector* cumulative_histogram = histogram.cumulative();
 
 	// The largest value in the cumulative histogram is the total pixel count
-	// of the source image. We need to scale this down to 255 to match to pixel
+	// of the source image. We need to scale this down to 255 to match the pixel
 	// format of the image.
 	double factor = 255.0/(double)(image.width()*image.height());
 
@@ -48,7 +48,7 @@ int main(int argc, char** argv) {
 	for(int x = 0; x < image.width(); x++) {
 		for(int y=0; y < image.height(); y++) {
 			// This is the old gray value
-			Ipp8s pixel = image.getPixelQuick(x, y);
+			Ipp8u pixel = image.getPixelQuick(x, y);
 
 			// We use it as and index into the cumulative histogram to
 			// computer the new one. This has to be scaled appropriately
@@ -58,10 +58,11 @@ int main(int argc, char** argv) {
 
 			// The pixel format is 8-bit integer, so we have to convert
 			// the result
-			Ipp8s new_pixel = (Ipp8s)(new_pixel_f * 255.0);
+			Ipp8u new_pixel = static_cast<Ipp8u>(new_pixel_f + 0.5);
 
 			// ..and save it
 			image.setPixelQuick(x, y, new_pixel);
+
 		}
 	}
 

BIN
core/tutorial/screenshots/tut02_eq_hist_new.png


BIN
core/tutorial/screenshots/tut02_eq_hist_org.png


BIN
core/tutorial/screenshots/tut02_eq_new.png


BIN
core/tutorial/screenshots/tut02_eq_org.png