diff --git a/Data/Baseline/OTB-Applications/Images/apTvClKMeansImageClassificationInputCentroids.tif b/Data/Baseline/OTB-Applications/Images/apTvClKMeansImageClassificationInputCentroids.tif new file mode 100644 index 0000000000000000000000000000000000000000..ce0ecbe5e6e8ea678c4ce3a6e7701d725de1658a --- /dev/null +++ b/Data/Baseline/OTB-Applications/Images/apTvClKMeansImageClassificationInputCentroids.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28427a2e951d1b56636568b966922a2d99f68c8e3f477aab99904fa5971fc42d +size 66540 diff --git a/Data/Input/Classification/KMeansInputCentroids.txt b/Data/Input/Classification/KMeansInputCentroids.txt new file mode 100644 index 0000000000000000000000000000000000000000..d7302a51166ea6468ac049ad970582370605c10a --- /dev/null +++ b/Data/Input/Classification/KMeansInputCentroids.txt @@ -0,0 +1,5 @@ +148.1360412249 176.9065574064 79.2367424483 275.6865470422 +180.3646315623 255.4157568188 138.2565634726 656.5357728603 +187.5074713392 256.7055784897 121.8671939978 115.8660938389 +220.0887858502 326.8933399989 229.672560688 434.3589597278 +515.191687488 834.8626368509 642.6102022528 814.8945435557 diff --git a/Data/Input/qb_ExtractRoad_pretty.png b/Data/Input/qb_ExtractRoad_pretty.png new file mode 100644 index 0000000000000000000000000000000000000000..d4307ec6f7341c32901e56f56b41927d18eedefb --- /dev/null +++ b/Data/Input/qb_ExtractRoad_pretty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a01fe4d41a060407d2613ba9595fde7321aa77c6e44f6552bb83d77d0dcef34 +size 1334929 diff --git a/Data/Output/DEMToHotImageGenerator.png b/Data/Output/DEMToHotImageGenerator.png new file mode 100644 index 0000000000000000000000000000000000000000..12224d6f0908d6c273cf87b787de7cfdd0ac7620 --- /dev/null +++ b/Data/Output/DEMToHotImageGenerator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d36b4f16b0f0be1d4d2247de214510349da67d37ae928945940898904e5a0a85 +size 234062 diff --git a/Data/Output/DEMToRainbowImageGenerator.png b/Data/Output/DEMToRainbowImageGenerator.png new file mode 100644 index 0000000000000000000000000000000000000000..935878f01c767e1f0aebc632201d9e2ba89bcfac --- /dev/null +++ b/Data/Output/DEMToRainbowImageGenerator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd8b080e078549cdcfa949a31a286b30b7d6f1c96c923a977083a02855994256 +size 239121 diff --git a/Data/Output/DEMToReliefImageGenerator.png b/Data/Output/DEMToReliefImageGenerator.png new file mode 100644 index 0000000000000000000000000000000000000000..97820270f0a32bc9d4c104a05045287c8f631e88 --- /dev/null +++ b/Data/Output/DEMToReliefImageGenerator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a9ed1706ab135a72068d49d0671e4c64c038d738928485b3721d29851c56ff1 +size 333514 diff --git a/Data/Output/GomaSmallFrostFiltered.png b/Data/Output/GomaSmallFrostFiltered.png new file mode 100644 index 0000000000000000000000000000000000000000..4bb8d0aff26b48ac98cf4449cd8f7134ece2952f --- /dev/null +++ b/Data/Output/GomaSmallFrostFiltered.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8469a05c42dbe0468daa7f9355fa6af0d79fa81793c93d001eb6310bf5c0ade6 +size 26542 diff --git a/Data/Output/HillShadingColorExample.png b/Data/Output/HillShadingColorExample.png new file mode 100644 index 0000000000000000000000000000000000000000..ef71ef616873d212669e28f78d1ce307fed60284 --- /dev/null +++ b/Data/Output/HillShadingColorExample.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba2f71579dafa1317e7f036223d1d849f3dfd8eb3a3e699c6fa91d5226372ab2 +size 484983 diff --git a/Data/Output/HillShadingExample.png b/Data/Output/HillShadingExample.png new file mode 100644 index 0000000000000000000000000000000000000000..d86e845d681b010064d24657bfc5cb93bf2f23e1 --- /dev/null +++ b/Data/Output/HillShadingExample.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11d6eb5b30a7a0507a0c2b15275a69d583ae89c49da1ecb1fd29eb7e4aabbb5b +size 183418 diff --git a/Data/Output/MSClusteredOutput-pretty.png b/Data/Output/MSClusteredOutput-pretty.png new file mode 100644 index 0000000000000000000000000000000000000000..caa286e69843f35949a09bc8bc42019420bb4892 --- /dev/null +++ b/Data/Output/MSClusteredOutput-pretty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf676b3188f175f21ea758ccfb7f551eb9468398d67f926ef677a89ca68dc622 +size 105266 diff --git a/Data/Output/MSLabeledOutput-pretty.png b/Data/Output/MSLabeledOutput-pretty.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6a18fbe4105fb582189df496e3beb846c083c2 --- /dev/null +++ b/Data/Output/MSLabeledOutput-pretty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2abea57f9bd5d62e708f6971178d2a3dd94997db37ab0846a66d6d99d37152a +size 105590 diff --git a/Data/Output/PrintableExampleOutput1.jpg b/Data/Output/PrintableExampleOutput1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab0a5d601db100171d2b4eebccaa087276ebee1a --- /dev/null +++ b/Data/Output/PrintableExampleOutput1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:895c3c368f3daf3049ec2be7164e94f1e0fd3cdd52259bf99fb3b187fece5b3b +size 108726 diff --git a/Data/Output/PrintableExampleOutput2.jpg b/Data/Output/PrintableExampleOutput2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5254ab0bb4c6736dd2930eba0070ddaf28164488 --- /dev/null +++ b/Data/Output/PrintableExampleOutput2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57a2949eb07c569b5d0b474d2b9cf8e3f8de62260850dfc1a9b3614c57cdf5bb +size 116071 diff --git a/Data/Output/QB_Toulouse_Ortho_PAN_casted.png b/Data/Output/QB_Toulouse_Ortho_PAN_casted.png new file mode 100644 index 0000000000000000000000000000000000000000..23aee06b351d171c95dd0bf521b046c8ebb3fd63 --- /dev/null +++ b/Data/Output/QB_Toulouse_Ortho_PAN_casted.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca5363df6bf1a9fd48d17b033cd507ece65605bce33a8f3da8e3ba4af5f5e705 +size 182862 diff --git a/Data/Output/QB_Toulouse_Ortho_PAN_rescaled.png b/Data/Output/QB_Toulouse_Ortho_PAN_rescaled.png new file mode 100644 index 0000000000000000000000000000000000000000..6a70131af5f2a3b3e2d3eef3b5fcf88c7fcacec6 --- /dev/null +++ b/Data/Output/QB_Toulouse_Ortho_PAN_rescaled.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e333f7abd77db1ab2158975e767ed1ea94ff547747584be07f54a01eae0bb3fa +size 108051 diff --git a/Data/Output/buildingExtractionIndexed_scaled.png b/Data/Output/buildingExtractionIndexed_scaled.png new file mode 100644 index 0000000000000000000000000000000000000000..f5c8ecd3189513614019da20e01580e77f2eddc5 --- /dev/null +++ b/Data/Output/buildingExtractionIndexed_scaled.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90304742fc7c5d8d88cff5abc627954337856160799856adb5dcb352db484ddd +size 17406 diff --git a/Data/Output/buildingExtractionRGB.png b/Data/Output/buildingExtractionRGB.png new file mode 100644 index 0000000000000000000000000000000000000000..0dbdd3d556a7aabcd2c736d68e3fbedc8b53301e --- /dev/null +++ b/Data/Output/buildingExtractionRGB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a91156558f5de2353efe3d8e3b17ef2284e74200a0bd1fed5d36bffcf720fc7a +size 26026 diff --git a/Data/Output/qb_BandMath-pretty.jpg b/Data/Output/qb_BandMath-pretty.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bd7702f79d46340d9a6cf011270092ab6b1f22af --- /dev/null +++ b/Data/Output/qb_BandMath-pretty.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:280cbf6de4cfc271371df94f79bdef1b6fb5089225e0d5105a6ad494ae7be89e +size 67381 diff --git a/Documentation/Cookbook/Scripts/RunExamples.py b/Documentation/Cookbook/Scripts/RunExamples.py index e04ea62e62a0d781018c498504ebdfc72f25c100..41cb849897d494cf71cfc858e9dc2258bd673ff2 100755 --- a/Documentation/Cookbook/Scripts/RunExamples.py +++ b/Documentation/Cookbook/Scripts/RunExamples.py @@ -33,7 +33,7 @@ blacklist = [ "LAIAndPROSAILToSensorResponse" # does not run, wrong arguments ] -def run_example(otb_root, otb_data, name, dry_run): +def run_example(otb_root, name, dry_run): """ Run an example by name Assumes the current working directory is an OTB build @@ -71,6 +71,7 @@ def run_example(otb_root, otb_data, name, dry_run): print("$ " + binary + " " + " ".join(example_args)) if not dry_run: + otb_data = join(otb_root, "Data") # Make sure Output dir exists os.makedirs(join(otb_data, "Output"), exist_ok=True) @@ -81,19 +82,18 @@ def run_example(otb_root, otb_data, name, dry_run): def main(): parser = argparse.ArgumentParser(usage="Run one or all OTB cxx examples") parser.add_argument("otb_root", help="Path to otb repository") - parser.add_argument("otb_data", help="Path to otb-data repository") parser.add_argument("--name", type=str, help="Run only one example with then given name") parser.add_argument("-n", "--dry-run", action='store_true', help="Dry run, only print commands") parser.add_argument("-k", "--keep-going", action='store_true', help="Keep going after failing examples") args = parser.parse_args() if args.name: - run_example(args.otb_root, args.otb_data, args.name, dry_run=args.dry_run) + run_example(args.otb_root, args.name, dry_run=args.dry_run) else: list_of_examples =[os.path.splitext(os.path.basename(f))[0] for f in glob.glob(join(args.otb_root, "Examples/*/*.cxx"))] for name in list_of_examples: try: - run_example(args.otb_root, args.otb_data, name, dry_run=args.dry_run) + run_example(args.otb_root, name, dry_run=args.dry_run) except Exception as e: if args.keep_going: print("Warning:", e) diff --git a/Documentation/Cookbook/Scripts/otbGenerateExamplesRstDoc.py b/Documentation/Cookbook/Scripts/otbGenerateExamplesRstDoc.py index 95463f1191d2f8a44e23f2397445fae2a7028247..f534a97dcb0578ab7928a44c0ba1aa262acb244e 100644 --- a/Documentation/Cookbook/Scripts/otbGenerateExamplesRstDoc.py +++ b/Documentation/Cookbook/Scripts/otbGenerateExamplesRstDoc.py @@ -44,7 +44,7 @@ def generate_examples_index(rst_dir, list_of_examples): index_f = open(join(rst_dir, "Examples.rst"), "w") index_f.write(RstPageHeading("C++ Examples", 3, ref="cpp-examples")) - for tag, examples_filenames in tag_files.items(): + for tag, examples_filenames in sorted(tag_files.items()): tag_filename = join("Examples", tag + ".rst") index_f.write("\t" + tag_filename + "\n") diff --git a/Examples/BasicFilters/BandMathFilterExample.cxx b/Examples/BasicFilters/BandMathFilterExample.cxx index 7404742ba2b75dd6eee7323870c5d7be7cdfd112..98d13618c6bac6176ee144408c22d09c60876769 100644 --- a/Examples/BasicFilters/BandMathFilterExample.cxx +++ b/Examples/BasicFilters/BandMathFilterExample.cxx @@ -22,22 +22,6 @@ ./BandMathFilterExample Input/qb_RoadExtract.tif Output/RoadExtractBandMath.tif Output/qb_BandMath-pretty.jpg */ - -// This filter is based on the mathematical parser library muParser. -// The built in functions and operators list is available at: -// http://muparser.sourceforge.net/mup_features.html. -// -// In order to use this filter, at least one input image should be -// set. An associated variable name can be specified or not by using -// the corresponding SetNthInput method. For the nth input image, if -// no associated variable name has been specified, a default variable -// name is given by concatenating the letter "b" (for band) and the -// corresponding input index. -// -// The next step is to set the expression according to the variable -// names. For example, in the default case with three input images the -// following expression is valid: ``(b1+b2)*b3``. - #include "itkMacro.h" #include <iostream> @@ -65,11 +49,10 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } - // We start by the typedef needed for reading and + // We start by the typedefs needed for reading and // writing the images. The BandMathImageFilter class // works with Image as input, so we need to define additional // filters to extract each layer of the multispectral image. - typedef double PixelType; typedef otb::VectorImage<PixelType, 2> InputImageType; typedef otb::Image<PixelType, 2> OutputImageType; @@ -78,13 +61,12 @@ int main(int argc, char* argv[]) typedef otb::ImageFileReader<InputImageType> ReaderType; typedef otb::ImageFileWriter<OutputImageType> WriterType; - // We can now define the type for the filter: + // We can now define the type for the filter typedef otb::BandMathImageFilter<OutputImageType> FilterType; - // We instantiate the filter, the reader, and the writer: + // We instantiate the filter, the reader, and the writer ReaderType::Pointer reader = ReaderType::New(); WriterType::Pointer writer = WriterType::New(); - FilterType::Pointer filter = FilterType::New(); writer->SetInput(filter->GetOutput()); @@ -93,9 +75,9 @@ int main(int argc, char* argv[]) reader->UpdateOutputInformation(); - // We now need to extract each band from the input \doxygen{otb}{VectorImage}, - // it illustrates the use of the \doxygen{otb}{VectorImageToImageList}. - // Each extracted layer is an input to the \doxygen{otb}{BandMathImageFilter}: + // We now need to extract each band from the input VectorImage, + // it illustrates the use of the VectorImageToImageList. + // Each extracted layer is an input to the BandMathImageFilter VectorImageToImageListType::Pointer imageList = VectorImageToImageListType::New(); imageList->SetInput(reader->GetOutput()); @@ -111,8 +93,8 @@ int main(int argc, char* argv[]) // Now we can define the mathematical expression to perform on the layers (b1, b2, b3, b4). // The filter takes advantage of the parsing capabilities of the muParser library and // allows setting the expression as on a digital calculator. - // - // The expression below returns 255 if the ratio $(NIR-RED)/(NIR+RED)$ is greater than 0.4 and 0 if not. + + // The expression below returns 255 if the ratio (NIR-RED)/(NIR+RED) is greater than 0.4 and 0 if not. filter->SetExpression("if((b4-b3)/(b4+b3) > 0.4, 255, 0)"); #ifdef OTB_MUPARSER_HAS_CXX_LOGICAL_OPERATORS @@ -125,19 +107,7 @@ int main(int argc, char* argv[]) writer->Update(); // The muParser library also provides the possibility to extend existing built-in functions. For example, - // you can use the OTB expression "ndvi(b3, b4)" with the filter. In this instance, the mathematical expression would be - // \textit{if($ndvi(b3, b4)>0.4$, 255, 0)}, which would return the same result. - - // Figure~\ref{fig:BandMathImageFilter} shows the result of the threshold applied to the NDVI index - // of a Quickbird image. - // \begin{figure} - // \center - // \includegraphics[width=0.45\textwidth]{qb_ExtractRoad_pretty.eps} - // \includegraphics[width=0.45\textwidth]{qb_BandMath-pretty.eps} - // \itkcaption[Band Math]{From left to right: - // Original image, thresholded NDVI index.} - // \label{fig:BandMathImageFilter} - // \end{figure} + // you can use the OTB expression "ndvi(b3, b4)" with the filter. In this instance, the mathematical expression would be "if(ndvi(b3, b4)>0.4, 255, 0)", which would return the same result. typedef otb::Image<unsigned char, 2> OutputPrettyImageType; typedef otb::ImageFileWriter<OutputPrettyImageType> PrettyImageFileWriterType; @@ -151,6 +121,4 @@ int main(int argc, char* argv[]) prettyWriter->SetFileName(argv[3]); prettyWriter->Update(); - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/BandMathFilterExample.rst b/Examples/BasicFilters/BandMathFilterExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..47da3da8989b1e399f5a20222c1529de6e44d757 --- /dev/null +++ b/Examples/BasicFilters/BandMathFilterExample.rst @@ -0,0 +1,27 @@ +The :doxygen:`BandMathImageFilter` is based on the mathematical parser library muParser. +The built in functions and operators list is available at: +http://muparser.sourceforge.net/mup_features.html. + +In order to use this filter, at least one input image should be +set. An associated variable name can be specified or not by using +the corresponding ``SetNthInput`` method. For the nth input image, if +no associated variable name has been specified, a default variable +name is given by concatenating the letter "b" (for band) and the +corresponding input index. + +The next step is to set the expression according to the variable +names. For example, in the default case with three input images the +following expression is valid: ``(b1+b2)*b3``. + +.. |image1| image:: /Input/qb_ExtractRoad_pretty.png + +.. |image2| image:: /Output/qb_BandMath-pretty.jpg + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ + + NDVI of a Quickbird image computed with BandMathImageFilter + diff --git a/Examples/BasicFilters/DEMToRainbowExample.cxx b/Examples/BasicFilters/DEMToRainbowExample.cxx index f1cf3e83a3ca04a550f95b21bff3dbcbb325a763..fcceeae63cc27416400a727f6397176b82708a1c 100644 --- a/Examples/BasicFilters/DEMToRainbowExample.cxx +++ b/Examples/BasicFilters/DEMToRainbowExample.cxx @@ -33,19 +33,6 @@ ./DEMToRainbowExample Output/DEMToReliefImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM_srtm relief */ - -// In some situation, it is desirable to represent a gray scale image in color for easier -// interpretation. This is particularly the case if pixel values in the image are used -// to represent some data such as elevation, deformation map, -// interferogram. In this case, it is important to ensure that similar -// values will get similar colors. You can notice how this requirement -// differs from the previous case. -// -// The following example illustrates the use of the \doxygen{otb}{DEMToImageGenerator} class -// combined with the \doxygen{otb}{ScalarToRainbowRGBPixelFunctor}. You can refer to the -// source code or to section \ref{sec:ReadDEM} for the DEM conversion to image, -// we will focus on the color conversion part here. - #include "otbImageFileReader.h" #include "otbImageFileWriter.h" @@ -103,9 +90,9 @@ int main(int argc, char* argv[]) demToImage->SetOutputSpacing(spacing); - // As in the previous example, the \doxygen{itk}{ScalarToRGBColormapImageFilter} is + // The ScalarToRGBColormapImageFilter is // the filter in charge of calling the functor we specify to do the work for - // each pixel. Here it is the \doxygen{otb}{ScalarToRainbowRGBPixelFunctor}. + // each pixel. Here it is the ScalarToRainbowRGBPixelFunctor. typedef itk::ScalarToRGBColormapImageFilter<ImageType, RGBImageType> ColorMapFilterType; ColorMapFilterType::Pointer colormapper = ColorMapFilterType::New(); @@ -146,34 +133,5 @@ int main(int argc, char* argv[]) writer->SetInput(colormapper->GetOutput()); - try - { - writer->Update(); - } - catch (itk::ExceptionObject& excep) - { - std::cerr << "Exception caught !" << std::endl; - std::cerr << excep << std::endl; - } - catch (...) - { - std::cout << "Unknown exception !" << std::endl; - return EXIT_FAILURE; - } - - // Figure~\ref{fig:RAINBOW_FILTER} shows the effect of applying the filter to - // a gray scale image. - // - // \begin{figure} - // \center - // \includegraphics[width=0.44\textwidth]{pretty_DEMToImageGenerator.eps} - // \includegraphics[width=0.44\textwidth]{DEMToRainbowImageGenerator.eps} - // \includegraphics[width=0.44\textwidth]{DEMToHotImageGenerator.eps} - // \includegraphics[width=0.44\textwidth]{DEMToReliefImageGenerator.eps} - // \itkcaption[Grayscale to color]{The gray level DEM extracted from SRTM - // data (top-left) and the same area represented in color.} - // \label{fig:RAINBOW_FILTER} - // \end{figure} - - return EXIT_SUCCESS; + writer->Update(); } diff --git a/Examples/BasicFilters/DEMToRainbowExample.rst b/Examples/BasicFilters/DEMToRainbowExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..a1164c7cdf091ba8106c4c6161ce60a1c94d9a28 --- /dev/null +++ b/Examples/BasicFilters/DEMToRainbowExample.rst @@ -0,0 +1,23 @@ +In some situation, it is desirable to represent a gray scale image in color for easier +interpretation. This is particularly the case if pixel values in the image are used +to represent some data such as elevation, deformation map, +interferogram. In this case, it is important to ensure that similar +values will get similar colors. You can notice how this requirement +differs from the previous case. + +The following example illustrates the use of the :doxygen:`DEMToImageGenerator` +class combined with the `ScalarToRainbowRGBPixelFunctor`. You can refer to the +source code for the DEM conversion to image, we will focus on the color +conversion part here. + +.. |image1| image:: /Output/DEMToRainbowImageGenerator.png + +.. |image2| image:: /Output/DEMToHotImageGenerator.png + +.. |image3| image:: /Output/DEMToReliefImageGenerator.png + +.. _Figure1: + ++--------------------------+-------------------------+-------------------------+ +| |image1| | |image2| | |image3| | ++--------------------------+-------------------------+-------------------------+ diff --git a/Examples/BasicFilters/FrostImageFilter.cxx b/Examples/BasicFilters/FrostImageFilter.cxx index 13d12674854981e964a32c70fe8d60756cbb9681..913ac71a271f1fc1abb1b45243aacf1999da22e3 100644 --- a/Examples/BasicFilters/FrostImageFilter.cxx +++ b/Examples/BasicFilters/FrostImageFilter.cxx @@ -23,34 +23,6 @@ ./FrostImageFilter Input/GomaSmall.png Output/GomaSmallFrostFiltered.png 5 0.1 */ - -// This example illustrates the use of the \doxygen{otb}{FrostImageFilter}. -// This filter belongs to the family of the edge-preserving smoothing -// filters which are usually used for speckle reduction in radar -// images. -// -// This filter uses a negative exponential convolution kernel. -// The output of the filter for pixel p is: -// $ \hat I_{s}=\sum_{p\in\eta_{p}} m_{p}I_{p} $ -// -// where : $ m_{p}=\frac{KC_{s}^{2}\exp(-KC_{s}^{2}d_{s, p})}{\sum_{p\in\eta_{p}} KC_{s}^{2}\exp(-KC_{s}^{2}d_{s, p})} $ -// and $ d_{s, p}=\sqrt{(i-i_{p})^2+(j-j_{p})^2} $ -// -// \begin{itemize} -// \item $ K $ : the decrease coefficient -// \item $ (i, j)$ : the coordinates of the pixel inside the region -// defined by $ \eta_{s} $ -// \item $ (i_{p}, j_{p})$ : the coordinates of the pixels belonging to $ \eta_{p} \subset \eta_{s} $ -// \item $ C_{s}$ : the variation coefficient computed over $ \eta_{p}$ -// \end{itemize} -// -// -// -// Most of this example is similar to the previous one and only the differences -// will be highlighted. -// -// First, we need to include the header: - #include "otbFrostImageFilter.h" #include "otbImage.h" @@ -68,16 +40,12 @@ int main(int argc, char* argv[]) } typedef unsigned char PixelType; - typedef otb::Image<PixelType, 2> InputImageType; typedef otb::Image<PixelType, 2> OutputImageType; - // The filter can be instantiated using the image types defined previously. - + // The filter can be instantiated using the image types defined previously. typedef otb::FrostImageFilter<InputImageType, OutputImageType> FilterType; - typedef otb::ImageFileReader<InputImageType> ReaderType; - typedef otb::ImageFileWriter<OutputImageType> WriterType; ReaderType::Pointer reader = ReaderType::New(); @@ -87,22 +55,12 @@ int main(int argc, char* argv[]) writer->SetInput(filter->GetOutput()); reader->SetFileName(argv[1]); - // The image obtained with the reader is passed as input to the - // \doxygen{otb}{FrostImageFilter}. - // - // \index{otb::FrostImageFilter!SetInput()} - // \index{otb::FileImageReader!GetOutput()} - + // The image obtained with the reader is passed as input to the FrostImageFilter filter->SetInput(reader->GetOutput()); - // The method \code{SetRadius()} defines the size of the window to - // be used for the computation of the local statistics. The method - // \code{SetDeramp()} sets the $K$ coefficient. - // - // \index{otb::FrostImageFilter!SetRadius()} - // \index{otb::FrostImageFilter!SetDeramp()} - // \index{SetDeramp()!otb::FrostImageFilter} - + // The method SetRadius() defines the size of the window to + // be used for the computation of the local statistics. The method + // SetDeramp() sets the K coefficient. FilterType::SizeType Radius; Radius[0] = atoi(argv[3]); Radius[1] = atoi(argv[3]); @@ -112,22 +70,4 @@ int main(int argc, char* argv[]) writer->SetFileName(argv[2]); writer->Update(); - - // Figure~\ref{fig:FROST_FILTER} shows the result of applying the Frost - // filter to a SAR image. - // \begin{figure} - // \center - // \includegraphics[width=0.44\textwidth]{GomaSmall.eps} - // \includegraphics[width=0.44\textwidth]{GomaSmallFrostFiltered.eps} - // \itkcaption[Frost Filter Application]{Result of applying the - // \doxygen{otb}{FrostImageFilter} to a SAR image.} - // \label{fig:FROST_FILTER} - // \end{figure} - // - // \relatedClasses - // \begin{itemize} - // \item \doxygen{otb}{LeeImageFilter} - // \end{itemize} - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/FrostImageFilter.rst b/Examples/BasicFilters/FrostImageFilter.rst new file mode 100644 index 0000000000000000000000000000000000000000..2951e334e7a6c3c7d087e6ae37ad8c05d8c880bc --- /dev/null +++ b/Examples/BasicFilters/FrostImageFilter.rst @@ -0,0 +1,34 @@ +This example illustrates the use of the :doxygen:`FrostImageFilter`. +This filter belongs to the family of the edge-preserving smoothing +filters which are usually used for speckle reduction in radar +images. + +This filter uses a negative exponential convolution kernel. +The output of the filter for pixel p is: + +.. math:: + + \hat I_{s}=\sum_{p\in\eta_{p}} m_{p}I_{p} + + m_{p}=\frac{KC_{s}^{2}\exp(-KC_{s}^{2}d_{s, p})}{\sum_{p\in\eta_{p}} KC_{s}^{2}\exp(-KC_{s}^{2}d_{s, p})} + + d_{s, p}=\sqrt{(i-i_{p})^2+(j-j_{p})^2} + +where: + +* :math:`K`: the decrease coefficient +* :math:`(i, j)`: the coordinates of the pixel inside the region defined by :math:`\eta_{s}` +* :math:`(i_{p}, j_{p})`: the coordinates of the pixels belonging to :math:`\eta_{p} \subset \eta_{s}` +* :math:`C_{s}`: the variation coefficient computed over :math:`\eta_{p}` + +.. |image1| image:: /Input/GomaSmall.png + +.. |image2| image:: /Output/GomaSmallFrostFiltered.png + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ + + Result of applying the FrostImageFilter to a SAR image. diff --git a/Examples/BasicFilters/HillShadingExample.cxx b/Examples/BasicFilters/HillShadingExample.cxx index 1d58ca2f68482dc62bdfe16e8fca5e4a9ce76668..a281e9aef85d86116d5ca7c35b719cbd832e9e19 100644 --- a/Examples/BasicFilters/HillShadingExample.cxx +++ b/Examples/BasicFilters/HillShadingExample.cxx @@ -24,18 +24,6 @@ */ -// Visualization of digital elevation models (DEM) is often more intuitive by simulating a -// lighting source and generating the corresponding shadows. This principle is called -// hill shading. -// -// Using a simple functor \doxygen{otb}{HillShadingFunctor} and the DEM image generated -// using the \doxygen{otb}{DEMToImageGenerator} (refer to \ref{sec:ReadDEM}), you can easily -// obtain a representation of the DEM. Better yet, using the -// \doxygen{otb}{ScalarToRainbowRGBPixelFunctor}, combined with the -// \doxygen{otb}{ReliefColormapFunctor} you can easily generate the classic elevation maps. -// -// This example will focus on the shading itself. - #include "otbImageFileReader.h" #include "otbImageFileWriter.h" @@ -50,7 +38,6 @@ int main(int argc, char* argv[]) { - if (argc < 10) { std::cout << argv[0] << " <output_filename> <output_color_filename> " @@ -162,21 +149,8 @@ int main(int argc, char* argv[]) writer2->SetInput(multiply->GetOutput()); - try - { - writer->Update(); - writer2->Update(); - } - catch (itk::ExceptionObject& excep) - { - std::cerr << "Exception caught !" << std::endl; - std::cerr << excep << std::endl; - } - catch (...) - { - std::cout << "Unknown exception !" << std::endl; - return EXIT_FAILURE; - } + writer->Update(); + writer2->Update(); otb::WorldFile::Pointer worldFile = otb::WorldFile::New(); worldFile->SetLonOrigin(origin[0]); @@ -188,17 +162,4 @@ int main(int argc, char* argv[]) worldFile->Update(); worldFile->SetImageFilename(argv[2]); worldFile->Update(); - - // Figure~\ref{fig:HILL_SHADING} shows the hill shading result from SRTM data. - // - // \begin{figure} - // \center - // \includegraphics[width=0.44\textwidth]{HillShadingExample.eps} - // \includegraphics[width=0.44\textwidth]{HillShadingColorExample.eps} - // \itkcaption[Hill shading]{Hill shading obtained from SRTM data (left) and combined with - // the color representation (right)} - // \label{fig:HILL_SHADING} - // \end{figure} - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/HillShadingExample.rst b/Examples/BasicFilters/HillShadingExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..98e95d368c9306e06501e696483d743a41c01700 --- /dev/null +++ b/Examples/BasicFilters/HillShadingExample.rst @@ -0,0 +1,23 @@ +Visualization of digital elevation models (DEM) is often more intuitive by +simulating a lighting source and generating the corresponding shadows. This +principle is called hill shading. + +Using :doxygen:`HillShadingFilter` and the DEM image generated +using the :doxygen:`DEMToImageGenerator`, you can easily obtain a representation +of the DEM. Better yet, using the :doxygen-itk:`ScalarToRGBColormapImageFilter` +combined with the ``ReliefColormapFunctor`` you can easily generate the +classic elevation maps. + +This example will focus on the shading itself. + +.. |image1| image:: /Output/HillShadingExample.png + +.. |image2| image:: /Output/HillShadingColorExample.png + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ + + Hill shading obtained from SRTM data (left) and combined with the color representation (right) diff --git a/Examples/BasicFilters/IndexedToRGBExample.cxx b/Examples/BasicFilters/IndexedToRGBExample.cxx index adba63456d9ca821727c58f18b15b81477c563f0..d7a8226949d33aa41c6e1a43f8c0ed00dc8f453b 100644 --- a/Examples/BasicFilters/IndexedToRGBExample.cxx +++ b/Examples/BasicFilters/IndexedToRGBExample.cxx @@ -24,25 +24,11 @@ */ -// Some algorithms produce an indexed image as output. In such images, -// each pixel is given a value according to the region number it belongs to. -// This value starting at 0 or 1 is usually an integer value. -// Often, such images are produced by segmentation or classification algorithms. -// -// If such regions are easy to manipulate -- it is easier and faster to compare two integers -// than a RGB value -- it is different when it comes to displaying the results. -// -// Here we present a convient way to convert such indexed image to a color image. In -// such conversion, it is important to ensure that neighborhood region, which are -// likely to have consecutive number have easily dicernable colors. This is done -// randomly using a hash function by the \doxygen{itk}{ScalarToRGBPixelFunctor}. - #include "otbImage.h" #include "otbImageFileReader.h" #include "otbImageFileWriter.h" #include "itkUnaryFunctorImageFilter.h" #include "itkScalarToRGBPixelFunctor.h" - #include "itkRescaleIntensityImageFilter.h" int main(int argc, char* argv[]) @@ -65,10 +51,8 @@ int main(int argc, char* argv[]) reader->SetFileName(inputFilename); - // The \doxygen{itk}{UnaryFunctorImageFilter} is the filter in charge of - // calling the functor we specify to do the work for each pixel. Here it is the - // \doxygen{itk}{ScalarToRGBPixelFunctor}. - + // The UnaryFunctorImageFilter is the filter in charge of calling the functor + // we specify to do the work for each pixel. Here it is the ScalarToRGBPixelFunctor typedef itk::Functor::ScalarToRGBPixelFunctor<unsigned long> ColorMapFunctorType; typedef itk::UnaryFunctorImageFilter<ImageType, RGBImageType, ColorMapFunctorType> ColorMapFilterType; ColorMapFilterType::Pointer colormapper = ColorMapFilterType::New(); @@ -93,17 +77,4 @@ int main(int argc, char* argv[]) writer2->SetFileName(outputScaledFilename); writer2->SetInput(rescaler->GetOutput()); writer2->Update(); - - // Figure~\ref{fig:INDEXTORGB_FILTER} shows the result of the conversion - // from an indexed image to a color image. - // \begin{figure} - // \center - // \includegraphics[width=0.44\textwidth]{buildingExtractionIndexed_scaled.eps} - // \includegraphics[width=0.44\textwidth]{buildingExtractionRGB.eps} - // \itkcaption[Scaling images]{The original indexed image (left) and the - // conversion to color image.} - // \label{fig:INDEXTORGB_FILTER} - // \end{figure} - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/IndexedToRGBExample.rst b/Examples/BasicFilters/IndexedToRGBExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..b9c02e70cc8661bed202df3aecf4e2bb90196459 --- /dev/null +++ b/Examples/BasicFilters/IndexedToRGBExample.rst @@ -0,0 +1,24 @@ +Some algorithms produce an indexed image as output. In such images, +each pixel is given a value according to the region number it belongs to. +This value starting at 0 or 1 is usually an integer value. +Often, such images are produced by segmentation or classification algorithms. + +If such regions are easy to manipulate -- it is easier and faster to compare two integers +than a RGB value -- it is different when it comes to displaying the results. + +Here we present a convient way to convert such indexed image to a color image. In +such conversion, it is important to ensure that neighboring regions, which are +likely to have consecutive number have easily dicernable colors. This is done +randomly using a hash function by ``ScalarToRGBPixelFunctor``. + +.. |image1| image:: /Output/buildingExtractionIndexed_scaled.png + +.. |image2| image:: /Output/buildingExtractionRGB.png + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ + + The original indexed image (left) and the conversion to color image. diff --git a/Examples/BasicFilters/LeeImageFilter.cxx b/Examples/BasicFilters/LeeImageFilter.cxx index fee4c62c8772beb4c285aa855faaca4dcbc88579..d8c7aa947db0bbde16790f0024a444c1deb3c40c 100644 --- a/Examples/BasicFilters/LeeImageFilter.cxx +++ b/Examples/BasicFilters/LeeImageFilter.cxx @@ -47,16 +47,16 @@ int main(int argc, char* argv[]) // The filter can be instantiated using the image types defined above. typedef otb::LeeImageFilter<InputImageType, OutputImageType> FilterType; - // An ImageFileReader class is also instantiated in order to read - // image data from a file. + // An ImageFileReader class is also instantiated in order to read + // image data from a file. typedef otb::ImageFileReader<InputImageType> ReaderType; - // An \doxygen{otb}{ImageFileWriter} is instantiated in order to write the + // An ImageFileWriter is instantiated in order to write the // output image to a file. typedef otb::ImageFileWriter<OutputImageType> WriterType; - // Both the filter and the reader are created by invoking their \code{New()} - // methods and assigning the result to SmartPointers. + // Both the filter and the reader are created by invoking their New() + // methods and assigning the result to SmartPointers. ReaderType::Pointer reader = ReaderType::New(); FilterType::Pointer filter = FilterType::New(); @@ -64,8 +64,8 @@ int main(int argc, char* argv[]) writer->SetInput(filter->GetOutput()); reader->SetFileName(argv[1]); - // The image obtained with the reader is passed as input to the - // LeeImageFilter. + // The image obtained with the reader is passed as input to the + // LeeImageFilter. filter->SetInput(reader->GetOutput()); // The method SetRadius() defines the size of the window to diff --git a/Examples/BasicFilters/LeeImageFilter.rst b/Examples/BasicFilters/LeeImageFilter.rst index bb34942a9ec560a722e01b71309dc570862d30b1..612bd8eb9ea84d5ced38c4dc92151aa77815c598 100644 --- a/Examples/BasicFilters/LeeImageFilter.rst +++ b/Examples/BasicFilters/LeeImageFilter.rst @@ -1,12 +1,19 @@ -This example illustrates the use of the LeeImageFilter. +This example illustrates the use of the :doxygen:`LeeImageFilter`. This filter belongs to the family of the edge-preserving smoothing filters which are usually used for speckle reduction in radar images. The LeeFilter aplies a linear regression which minimizes the mean-square error in the frame of a multiplicative speckle model. -.. figure:: /Input/GomaSmall.png +.. |image1| image:: /Input/GomaSmall.png -.. figure:: /Output/GomaSmallLeeFiltered.png +.. |image2| image:: /Output/GomaSmallLeeFiltered.png + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ Result of applying the Lee filter to a SAR image. + diff --git a/Examples/BasicFilters/MeanShiftSegmentationFilterExample.cxx b/Examples/BasicFilters/MeanShiftSegmentationFilterExample.cxx index f3992ead4089875bee8132029e4278585e214d66..9e665a35f21b216bc11ea6f433a1534233ee0f5e 100644 --- a/Examples/BasicFilters/MeanShiftSegmentationFilterExample.cxx +++ b/Examples/BasicFilters/MeanShiftSegmentationFilterExample.cxx @@ -32,29 +32,15 @@ 0.1 */ - -// This example demonstrates the use of the -// \doxygen{otb}{MeanShiftSegmentationFilter} class which implements -// filtering and clustering using the mean shift algorithm -// \cite{Comaniciu2002}. For a given pixel, the mean shift will -// build a set of neighboring pixels within a given spatial radius -// and a color range. The spatial and color center of this set is -// then computed and the algorithm iterates with this new spatial and -// color center. The Mean Shift can be used for edge-preserving -// smoothing, or for clustering. - #include "otbVectorImage.h" #include "otbImageFileReader.h" #include "otbImageFileWriter.h" #include "otbImageFileWriter.h" #include "otbPrintableImageFilter.h" - #include "itkScalarToRGBPixelFunctor.h" #include "itkUnaryFunctorImageFilter.h" - -// We start by including the needed header file. - #include "otbMeanShiftSegmentationFilter.h" + int main(int argc, char* argv[]) { if (argc != 11) @@ -75,9 +61,6 @@ int main(int argc, char* argv[]) const unsigned int maxiter = atoi(argv[9]); const double thres = atof(argv[10]); - // We start by the classical \code{typedef}s needed for reading and - // writing the images. - const unsigned int Dimension = 2; typedef float PixelType; @@ -94,36 +77,39 @@ int main(int argc, char* argv[]) typedef otb::MeanShiftSegmentationFilter<ImageType, LabelImageType, ImageType> FilterType; - // We instantiate the filter, the reader, and 2 writers (for the - // labeled and clustered images). + // We instantiate the filter, the reader, and 2 writers (for the + // labeled and clustered images). FilterType::Pointer filter = FilterType::New(); ReaderType::Pointer reader = ReaderType::New(); WriterType::Pointer writer1 = WriterType::New(); LabelWriterType::Pointer writer2 = LabelWriterType::New(); - // We set the file names for the reader and the writers: - + // We set the file names for the reader and the writers: reader->SetFileName(infname); writer1->SetFileName(clusteredfname); writer2->SetFileName(labeledfname); - // We can now set the parameters for the filter. There are 3 main - // parameters: the spatial radius used for defining the neighborhood, - // the range radius used for defining the interval in the color space - // and the minimum size for the regions to be kept after clustering. + // We can now set the parameters for the filter. There are 3 main + // parameters: the spatial radius used for defining the neighborhood, + // the range radius used for defining the interval in the color space + // and the minimum size for the regions to be kept after clustering. filter->SetSpatialBandwidth(spatialRadius); filter->SetRangeBandwidth(rangeRadius); filter->SetMinRegionSize(minRegionSize); - // Two another parameters can be set : the maximum iteration number, which defines maximum number of iteration until convergence. - // Algorithm iterative scheme will stop if convergence hasn't been reached after the maximum number of iterations. - // Threshold parameter defines mean-shift vector convergence value. Algorithm iterative scheme will stop if mean-shift vector is below this threshold or if - // iteration number reached maximum number of iterations. + + // Two another parameters can be set: the maximum iteration number, which + // defines maximum number of iteration until convergence. Algorithm + // iterative scheme will stop if convergence hasn't been reached after the + // maximum number of iterations. Threshold parameter defines mean-shift + // vector convergence value. Algorithm iterative scheme will stop if + // mean-shift vector is below this threshold or if iteration number reached + // maximum number of iterations. filter->SetMaxIterationNumber(maxiter); filter->SetThreshold(thres); - // We can now plug the pipeline and run it. + // We can now plug the pipeline and run it. filter->SetInput(reader->GetOutput()); writer1->SetInput(filter->GetClusteredOutput()); @@ -132,19 +118,6 @@ int main(int argc, char* argv[]) writer1->Update(); writer2->Update(); - // Figure~\ref{fig:MeanShiftSegmentationFilter} shows the result of applying the mean shift - // to a Quickbird image. - // \begin{figure} - // \center - // \includegraphics[width=0.40\textwidth]{ROI_QB_MUL_1.eps} - // \includegraphics[width=0.40\textwidth]{MSClusteredOutput-pretty.eps} - // \includegraphics[width=0.40\textwidth]{MSLabeledOutput-pretty.eps} - // \itkcaption[Mean Shift]{From top to bottom and left to right: - // Original image, image filtered by - // mean shift after clustering , and labeled image.} - // \label{fig:MeanShiftSegmentationFilter} - // \end{figure} - typedef otb::PrintableImageFilter<ImageType> PrintableFilterType; PrintableFilterType::Pointer printableImageFilter = PrintableFilterType::New(); @@ -176,6 +149,4 @@ int main(int argc, char* argv[]) labelRGBWriter->SetFileName(labeledpretty); labelRGBWriter->SetInput(labelToRGB->GetOutput()); labelRGBWriter->Update(); - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/MeanShiftSegmentationFilterExample.rst b/Examples/BasicFilters/MeanShiftSegmentationFilterExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..48e8fc09402b1907bc67ae3c8099dbbb2170cc9c --- /dev/null +++ b/Examples/BasicFilters/MeanShiftSegmentationFilterExample.rst @@ -0,0 +1,21 @@ +This example demonstrates the use of the :doxygen:`MeanShiftSegmentationFilter` +class which implements filtering and clustering using the mean shift algorithm. +For a given pixel, the mean shift will build a set of neighboring pixels within +a given spatial radius and a color range. The spatial and color center of this +set is then computed and the algorithm iterates with this new spatial and color +center. The Mean Shift can be used for edge-preserving smoothing, or for +clustering. + +.. |image1| image:: /Input/ROI_QB_MUL_1.png + +.. |image2| image:: /Output/MSClusteredOutput-pretty.png + +.. |image3| image:: /Output/MSLabeledOutput-pretty.png + +.. _Figure1: + ++--------------------------+-------------------------+-------------------------+ +| |image1| | |image2| | |image3| | ++--------------------------+-------------------------+-------------------------+ + + Original image, image filtered by mean shift after clustering, and labeled image. diff --git a/Examples/BasicFilters/PrintableImageFilterExample.cxx b/Examples/BasicFilters/PrintableImageFilterExample.cxx index abe40efdbbb6d00a6e6009e7296ddb5882feb802..f632da523ccbc7faa000df846524fe12d88f3f65 100644 --- a/Examples/BasicFilters/PrintableImageFilterExample.cxx +++ b/Examples/BasicFilters/PrintableImageFilterExample.cxx @@ -29,30 +29,6 @@ */ -// Most of the time, satellite images have more than three spectral bands. As we -// are only able to see three colors (red, green and blue), we have to find a way to -// represent these images using only three bands. This is called creating a color -// composition. -// -// Of course, any color composition will not be able to render all the information -// available in the original image. As a consequence, sometimes, creating more than -// one color composition will be necessary. -// -// If you want to obtain an image with natural colors, you have to match the wavelength -// captured by the satellite with those captured by your eye: thus matching the red band -// with the red color, etc. -// -// Some satellites (SPOT 5 is an example) do not acquire all the {\em human} spectral bands: -// the blue can be missing and replaced by some other wavelength of interest for a specific application. -// In these situations, another mapping has to be created. That's why, the vegetation often appears in -// red in satellite images (see on left of figure~\ref{fig:PRINTABLE_FILTER}). -// -// The band order in the image products can be also quite tricky. It could be in the wavelength order, -// as it is the case for Quickbird (1: Blue, 2: Green, 3: Red, 4: NIR), in this case, you -// have to be careful to reverse the order if you want a natural display. It could also be reverse -// to facilitate direct viewing, as for SPOT5 (1: NIR, 2: Red, 3: Green, 4: SWIR) but in this situations -// you have to be careful when you process the image. - #include "otbVectorImage.h" #include "otbImageFileReader.h" #include "otbImageFileWriter.h" @@ -84,9 +60,8 @@ int main(int argc, char* argv[]) ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(inputFilename); - // To easily convert the image to a {\em printable} format, i.e. 3 bands - // \code{unsigned char} value, you can use the \doxygen{otb}{PrintableImageFilter}. - + // To easily convert the image to a printable format, i.e. 3 bands + // unsigned char value, you can use the PrintableImageFilter. typedef otb::PrintableImageFilter<InputImageType> PrintableFilterType; PrintableFilterType::Pointer printableImageFilter = PrintableFilterType::New(); @@ -95,9 +70,8 @@ int main(int argc, char* argv[]) printableImageFilter->SetChannel(greenChannelNumber); printableImageFilter->SetChannel(blueChannelNumber); - // When you create the writer to plug at the output of the \code{printableImageFilter} + // When you create the writer to plug at the output of the printableImageFilter // you may want to use the direct type definition as it is a good way to avoid mismatch: - typedef PrintableFilterType::OutputImageType OutputImageType; typedef otb::ImageFileWriter<OutputImageType> WriterType; @@ -106,17 +80,4 @@ int main(int argc, char* argv[]) writer->SetInput(printableImageFilter->GetOutput()); writer->Update(); - - // Figure~\ref{fig:PRINTABLE_FILTER} illustrates different color compositions for a SPOT 5 image. - // \begin{figure} - // \center - // \includegraphics[width=0.44\textwidth]{PrintableExampleOutput1.eps} - // \includegraphics[width=0.44\textwidth]{PrintableExampleOutput2.eps} - // \itkcaption[Scaling images]{On the left, a classic SPOT5 - // combination: XS3 in red, XS2 in green and XS1 in blue. On the - // right another composition: XS3 in red, XS4 in green and XS2 in blue.} - // \label{fig:PRINTABLE_FILTER} - // \end{figure} - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/PrintableImageFilterExample.rst b/Examples/BasicFilters/PrintableImageFilterExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..9c503fa73bb81e54857e01cdac3e3fce27c071bf --- /dev/null +++ b/Examples/BasicFilters/PrintableImageFilterExample.rst @@ -0,0 +1,37 @@ +Most of the time, satellite images have more than three spectral bands. As we +are only able to see three colors (red, green and blue), we have to find a way +to represent these images using only three bands. This is called creating a +color composition. + +Of course, any color composition will not be able to render all the information +available in the original image. As a consequence, sometimes, creating more than +one color composition will be necessary. + +If you want to obtain an image with natural colors, you have to match the +wavelength captured by the satellite with those captured by your eye: thus +matching the red band with the red color, etc. + +Some satellites (SPOT 5 is an example) do not acquire all the visible +spectral bands: the blue can be missing and replaced by some other wavelength of +interest for a specific application. In these situations, another mapping has +to be created. That's why the vegetation often appears in red in satellite +images. + +The band order in the image products can be also quite tricky. It could be in +the wavelength order, as it is the case for Quickbird (1: Blue, 2: Green, 3: +Red, 4: NIR), in this case, you have to be careful to reverse the order if you +want a natural display. It could also be reverse to facilitate direct viewing, +as for SPOT5 (1: NIR, 2: Red, 3: Green, 4: SWIR) but in this situations you have +to be careful when you process the image. + +.. |image1| image:: /Output/PrintableExampleOutput1.jpg + +.. |image2| image:: /Output/PrintableExampleOutput2.jpg + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ + +On the left, a classic SPOT5 combination: XS3 in red, XS2 in green and XS1 in blue. On the right another composition: XS3 in red, XS4 in green and XS2 in blue. diff --git a/Examples/BasicFilters/ScalingFilterExample.cxx b/Examples/BasicFilters/ScalingFilterExample.cxx index 174fa6699ca6a13dfd88f1d28c79e03ca960d1b5..feef4ec7231e8f0e1a456196a52b04e59daa7f1e 100644 --- a/Examples/BasicFilters/ScalingFilterExample.cxx +++ b/Examples/BasicFilters/ScalingFilterExample.cxx @@ -23,13 +23,6 @@ ./ScalingFilterExample Input/QB_Toulouse_Ortho_PAN.tif Output/QB_Toulouse_Ortho_PAN_rescaled.png Output/QB_Toulouse_Ortho_PAN_casted.png */ - -// On one hand, satellite images are commonly coded on more than 8 bits to provide -// the dynamic range required from shadows to clouds. On the other hand, image formats -// in use for printing and display are usually limited to 8 bits. We need to convert the value -// to enable a proper display. This is usually done using linear scaling. Of course, you have -// to be aware that some information is lost in the process. - #include "otbImage.h" #include "otbImageFileReader.h" #include "otbImageFileWriter.h" @@ -56,8 +49,7 @@ int main(int argc, char* argv[]) ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(argv[1]); - // The \doxygen{itk}{RescaleIntensityImageFilter} is used to rescale the value: - + // The RescaleIntensityImageFilter is used to rescale the value typedef itk::RescaleIntensityImageFilter<InputImageType, OutputImageType> RescalerType; RescalerType::Pointer rescaler = RescalerType::New(); rescaler->SetInput(reader->GetOutput()); @@ -75,20 +67,4 @@ int main(int argc, char* argv[]) writer->SetFileName(argv[3]); writer->SetInput(caster->GetOutput()); writer->Update(); - - // Figure~\ref{fig:SCALING_FILTER} illustrates the difference between a proper scaling and - // a simple truncation of the value and demonstrates why it is - // important to keep this in mind. - // \begin{figure} - // \center - // \includegraphics[width=0.44\textwidth]{QB_Toulouse_Ortho_PAN_casted.eps} - // \includegraphics[width=0.44\textwidth]{QB_Toulouse_Ortho_PAN_rescaled.eps} - // \itkcaption[Scaling images]{On the left, the image obtained by truncated pixel values - // at the dynamic acceptable for a png file (between 0 and 255). On the right, - // the same image with - // a proper rescaling} - // \label{fig:SCALING_FILTER} - // \end{figure} - - return EXIT_SUCCESS; } diff --git a/Examples/BasicFilters/ScalingFilterExample.rst b/Examples/BasicFilters/ScalingFilterExample.rst new file mode 100644 index 0000000000000000000000000000000000000000..a284ff97abf3eef2c906d4040e28990aa48acad1 --- /dev/null +++ b/Examples/BasicFilters/ScalingFilterExample.rst @@ -0,0 +1,17 @@ +On one hand, satellite images are commonly coded on more than 8 bits to provide +the dynamic range required from shadows to clouds. On the other hand, image formats +in use for printing and display are usually limited to 8 bits. We need to convert the value +to enable a proper display. This is usually done using linear scaling. Of course, you have +to be aware that some information is lost in the process. + +.. |image1| image:: /Output/QB_Toulouse_Ortho_PAN_casted.png + +.. |image2| image:: /Output/QB_Toulouse_Ortho_PAN_rescaled.png + +.. _Figure1: + ++--------------------------+-------------------------+ +| |image1| | |image2| | ++--------------------------+-------------------------+ + + On the left, the image obtained by truncated pixel values at the dynamic acceptable for a png file (between 0 and 255). On the right, the same image with a proper rescaling. diff --git a/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx b/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx index bcb546e03d11118b8e5c98a29d1cc7dbd824873e..d1c7fcd52f0e88e315536ec6afc7343f1ca9d5bd 100644 --- a/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx +++ b/Modules/Applications/AppClassification/app/otbKMeansClassification.cxx @@ -77,13 +77,22 @@ protected: MandatoryOff("ts"); AddParameter(ParameterType_Int, "maxit", "Maximum number of iterations"); - SetParameterDescription("maxit", "Maximum number of iterations for the learning step."); + SetParameterDescription("maxit", + "Maximum number of iterations for the learning step." + " If this parameter is set to 0, the KMeans algorithm will not stop until convergence"); SetDefaultParameterInt("maxit", 1000); MandatoryOff("maxit"); - AddParameter(ParameterType_OutputFilename, "outmeans", "Centroid filename"); - SetParameterDescription("outmeans", "Output text file containing centroid positions"); - MandatoryOff("outmeans"); + AddParameter(ParameterType_Group, "centroids", "Centroids IO parameters"); + SetParameterDescription("centroids", "Group of parameters for centroids IO."); + + AddParameter(ParameterType_InputFilename, "centroids.in", "input centroids text file"); + SetParameterDescription("centroids.in", + "Input text file containing centroid positions used to initialize the algorithm. " + "Each centroid must be described by p parameters, p being the number of bands in " + "the input image, and the number of centroids must be equal to the number of classes " + "(one centroid per line with values separated by spaces)."); + MandatoryOff("centroids.in"); ShareKMSamplingParameters(); ConnectKMSamplingParams(); @@ -99,6 +108,7 @@ protected: { ShareParameter("ram", "polystats.ram"); ShareParameter("sampler", "select.sampler"); + ShareParameter("centroids.out", "training.classifier.sharkkm.centroids.out"); ShareParameter("vm", "polystats.mask", "Validity Mask", "Validity mask, only non-zero pixels will be used to estimate KMeans modes."); } @@ -248,6 +258,14 @@ protected: GetParameterInt("maxit")); GetInternalApplication("training")->SetParameterInt("classifier.sharkkm.k", GetParameterInt("nc")); + if (IsParameterEnabled("centroids.in") && HasValue("centroids.in")) + { + GetInternalApplication("training")->SetParameterString("classifier.sharkkm.centroids.in", GetParameterString("centroids.in")); + + GetInternalApplication("training") + ->SetParameterString("classifier.sharkkm.centroids.stats", GetInternalApplication("imgstats")->GetParameterString("out")); + } + if( IsParameterEnabled("rand")) GetInternalApplication("training")->SetParameterInt("rand", GetParameterInt("rand")); @@ -276,55 +294,6 @@ protected: ExecuteInternal( "classif" ); } - void CreateOutMeansFile(FloatVectorImageType *image, - const std::string &modelFileName, - unsigned int nbClasses) - { - if (IsParameterEnabled("outmeans")) - { - unsigned int nbBands = image->GetNumberOfComponentsPerPixel(); - unsigned int nbElements = nbClasses * nbBands; - // get the line in model file that contains the centroids positions - std::ifstream infile(modelFileName); - if(!infile) - { - itkExceptionMacro(<< "File: " << modelFileName << " couldn't be opened"); - } - - // get the line with the centroids (starts with "2 ") - std::string line, centroidLine; - while(std::getline(infile,line)) - { - if (line.size() > 2 && line[0] == '2' && line[1] == ' ') - { - centroidLine = line; - break; - } - } - - std::vector<std::string> centroidElm; - boost::split(centroidElm,centroidLine,boost::is_any_of(" ")); - - // remove the first elements, not the centroids positions - int nbWord = centroidElm.size(); - int beginCentroid = nbWord-nbElements; - centroidElm.erase(centroidElm.begin(), centroidElm.begin()+beginCentroid); - - // write in the output file - std::ofstream outfile; - outfile.open(GetParameterString("outmeans")); - - for (unsigned int i = 0; i < nbClasses; i++) - { - for (unsigned int j = 0; j < nbBands; j++) - { - outfile << std::setw(8) << centroidElm[i * nbBands + j] << " "; - } - outfile << std::endl; - } - } - } - class KMeansFileNamesHandler { public: @@ -495,9 +464,6 @@ private: // Compute a classification of the input image according to a model file Superclass::KMeansClassif(); - // Create the output text file containing centroids positions - Superclass::CreateOutMeansFile(GetParameterImage("in"), fileNames.modelFile, GetParameterInt("nc")); - // Remove all tempory files if( GetParameterInt( "cleanup" ) ) { diff --git a/Modules/Applications/AppClassification/include/otbLearningApplicationBase.hxx b/Modules/Applications/AppClassification/include/otbLearningApplicationBase.hxx index 8d76ee548aff28eb61f5febe1b7b248b96ec47da..0e56b0c6d14ea0c6665461d76ed24b5c2170dc83 100644 --- a/Modules/Applications/AppClassification/include/otbLearningApplicationBase.hxx +++ b/Modules/Applications/AppClassification/include/otbLearningApplicationBase.hxx @@ -122,7 +122,10 @@ LearningApplicationBase<TInputValue,TOutputValue> ::InitUnsupervisedClassifierParams() { #ifdef OTB_USE_SHARK - InitSharkKMeansParams(); + if (!m_RegressionFlag) + { + InitSharkKMeansParams(); // Regression not supported + } #endif } diff --git a/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.hxx b/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.hxx index a3c43741c70ace5c453ae45212abedb313b30b32..87ff3a948edc75658b5223780aec8e60788e0afa 100644 --- a/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.hxx +++ b/Modules/Applications/AppClassification/include/otbTrainSharkKMeans.hxx @@ -22,6 +22,7 @@ #include "otbLearningApplicationBase.h" #include "otbSharkKMeansMachineLearningModel.h" +#include "otbStatisticsXMLFileReader.h" namespace otb { @@ -44,6 +45,30 @@ void LearningApplicationBase<TInputValue, TOutputValue>::InitSharkKMeansParams() SetParameterInt("classifier.sharkkm.k", 2); SetParameterDescription("classifier.sharkkm.k", "The number of classes used for the kmeans algorithm. Default set to 2 class"); SetMinimumParameterIntValue("classifier.sharkkm.k", 2); + + // Centroid IO + AddParameter( ParameterType_Group, "classifier.sharkkm.centroids", "Centroids IO parameters" ); + SetParameterDescription( "classifier.sharkkm.centroids", "Group of parameters for centroids IO." ); + + // Input centroids + AddParameter(ParameterType_InputFilename, "classifier.sharkkm.centroids.in", "User definied input centroids"); + SetParameterDescription("classifier.sharkkm.centroids.in", "Input text file containing centroid posistions used to initialize the algorithm. " + "Each centroid must be described by p parameters, p being the number of features in " + "the input vector data, and the number of centroids must be equal to the number of classes " + "(one centroid per line with values separated by spaces)."); + MandatoryOff("classifier.sharkkm.centroids"); + + // Centroid statistics + AddParameter(ParameterType_InputFilename, "classifier.sharkkm.centroids.stats", "Statistics file"); + SetParameterDescription("classifier.sharkkm.centroids.stats", "A XML file containing mean and standard deviation to center" + "and reduce the centroids before the KMeans algorithm, produced by ComputeImagesStatistics application."); + MandatoryOff("classifier.sharkkm.centroids.stats"); + + // Output centroids + AddParameter(ParameterType_OutputFilename, "classifier.sharkkm.centroids.out", "Output centroids text file"); + SetParameterDescription("classifier.sharkkm.centroids.out", "Output text file containing centroids after the kmean algorithm."); + MandatoryOff("classifier.sharkkm.centroids.out"); + } template<class TInputValue, class TOutputValue> @@ -60,9 +85,48 @@ void LearningApplicationBase<TInputValue, TOutputValue>::TrainSharkKMeans( classifier->SetInputListSample( trainingListSample ); classifier->SetTargetListSample( trainingLabeledListSample ); classifier->SetK( k ); + + // Initialize centroids from file + if(IsParameterEnabled("classifier.sharkkm.centroids.in") && HasValue("classifier.sharkkm.centroids.in")) + { + shark::Data<shark::RealVector> centroidData; + shark::importCSV(centroidData, GetParameterString( "classifier.sharkkm.centroids.in"), ' '); + if( HasValue( "classifier.sharkkm.centroids.stats" ) ) + { + auto statisticsReader = otb::StatisticsXMLFileReader< itk::VariableLengthVector<float> >::New(); + statisticsReader->SetFileName(GetParameterString( "classifier.sharkkm.centroids.stats" )); + auto meanMeasurementVector = statisticsReader->GetStatisticVectorByName("mean"); + auto stddevMeasurementVector = statisticsReader->GetStatisticVectorByName("stddev"); + + // Convert itk Variable Length Vector to shark Real Vector + shark::RealVector offsetRV(meanMeasurementVector.Size()); + shark::RealVector scaleRV(stddevMeasurementVector.Size()); + + assert(meanMeasurementVector.Size()==stddevMeasurementVector.Size()); + for (unsigned int i = 0; i<meanMeasurementVector.Size(); ++i) + { + scaleRV[i] = 1/stddevMeasurementVector[i]; + // Substract the normalized mean + offsetRV[i] = - meanMeasurementVector[i]/stddevMeasurementVector[i]; + } + + shark::Normalizer<> normalizer(scaleRV, offsetRV); + centroidData = normalizer(centroidData); + } + + if (centroidData.numberOfElements() != k) + otbAppLogWARNING( "The input centroid file will not be used because it contains " << centroidData.numberOfElements() << + " points, which is different than from the requested number of class: " << k <<"."); + + classifier->SetCentroidsFromData( centroidData); + } + classifier->SetMaximumNumberOfIterations( nbMaxIter ); classifier->Train(); classifier->Save( modelPath ); + + if( HasValue( "classifier.sharkkm.centroids.out")) + classifier->ExportCentroids( GetParameterString( "classifier.sharkkm.centroids.out" )); } } //end namespace wrapper diff --git a/Modules/Applications/AppClassification/test/CMakeLists.txt b/Modules/Applications/AppClassification/test/CMakeLists.txt index 6a05178e64311574862ceaf555fcff1310111815..556cc57987594132598622a258bc9019e23037f7 100644 --- a/Modules/Applications/AppClassification/test/CMakeLists.txt +++ b/Modules/Applications/AppClassification/test/CMakeLists.txt @@ -673,7 +673,7 @@ if(OTB_USE_SHARK) -sampler periodic -rand 121212 -nodatalabel 255 - -outmeans ${TEMP}/apTvClKMeansImageClassificationFilterOutMeans.txt + -centroids.out ${TEMP}/apTvClKMeansImageClassificationFilterOutMeans.txt -out ${TEMP}/apTvClKMeansImageClassificationFilterOutput.tif uint8 -cleanup 0 VALID --compare-image ${NOTOL} @@ -681,6 +681,25 @@ if(OTB_USE_SHARK) ${TEMP}/apTvClKMeansImageClassificationFilterOutput.tif ) endif() +if(OTB_USE_SHARK) + otb_test_application(NAME apTvClKMeansImageClassification_inputCentroids + APP KMeansClassification + OPTIONS -in ${INPUTDATA}/qb_RoadExtract.img + -ts 30000 + -nc 5 + -maxit 10000 + -sampler periodic + -nodatalabel 255 + -rand 121212 + -centroids.in ${INPUTDATA}/Classification/KMeansInputCentroids.txt + -out ${TEMP}/apTvClKMeansImageClassificationInputCentroids.tif uint8 + -cleanup 0 + + VALID --compare-image ${NOTOL} + ${OTBAPP_BASELINE}/apTvClKMeansImageClassificationInputCentroids.tif + ${TEMP}/apTvClKMeansImageClassificationInputCentroids.tif ) +endif() + #----------- TrainImagesClassifier TESTS ---------------- if(OTB_USE_LIBSVM) otb_test_application(NAME apTvClTrainSVMImagesClassifierQB1_allOpt_InXML diff --git a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h index 77e8691cf6eb6298639156171113bec261d0e6de..69574043779cf5b6144a34f5b6e446721047ec2e 100644 --- a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h +++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.h @@ -48,6 +48,7 @@ #include "shark/Models/Clustering/Centroids.h" #include "shark/Models/Clustering/ClusteringModel.h" #include "shark/Algorithms/KMeans.h" +#include "shark/Models/Normalizer.h" #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop @@ -124,9 +125,14 @@ public: /** Set the number of class for the kMeans algorithm.*/ itkSetMacro( K, unsigned ); - /** If true, normalized input data sample list */ - itkGetMacro( Normalized, bool ); - itkSetMacro( Normalized, bool ); + /** Initialize the centroids for the kmeans algorithm */ + void SetCentroidsFromData(const shark::Data<shark::RealVector>& data) + { + m_Centroids.setCentroids(data); + this->Modified(); + } + + void ExportCentroids(const std::string& filename); protected: /** Constructor */ @@ -142,9 +148,6 @@ protected: virtual void DoPredictBatch(const InputListSampleType *, const unsigned int &startIndex, const unsigned int &size, TargetListSampleType *, ConfidenceListSampleType * = nullptr, ProbaListSampleType * = nullptr) const override; - template<typename DataType> - DataType NormalizeData(const DataType &data) const; - /** PrintSelf method */ void PrintSelf(std::ostream &os, itk::Indent indent) const override; @@ -153,16 +156,13 @@ private: void operator=(const Self &) = delete; // Parameters set by the user - bool m_Normalized; unsigned int m_K; unsigned int m_MaximumNumberOfIterations; bool m_CanRead; - /** Centroids results form kMeans */ shark::Centroids m_Centroids; - /** shark Model could be SoftClusteringModel or HardClusteringModel */ boost::shared_ptr<ClusteringModelType> m_ClusteringModel; diff --git a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.hxx b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.hxx index 3838d38aaa5e56bd4f69531711a7d6e1f45cc880..6554c67d318439e7f3d2f1560f9aa1eda5661957 100644 --- a/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.hxx +++ b/Modules/Learning/Unsupervised/include/otbSharkKMeansMachineLearningModel.hxx @@ -35,11 +35,10 @@ #include "otb_shark.h" #include "otbSharkUtils.h" -#include "shark/Algorithms/Trainers/NormalizeComponentsUnitVariance.h" //normalize #include "shark/Algorithms/KMeans.h" //k-means algorithm #include "shark/Models/Clustering/HardClusteringModel.h" #include "shark/Models/Clustering/SoftClusteringModel.h" -#include "shark/Algorithms/Trainers/NormalizeComponentsUnitVariance.h" +#include <shark/Data/Csv.h> //load the csv file #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop @@ -52,7 +51,7 @@ namespace otb template<class TInputValue, class TOutputValue> SharkKMeansMachineLearningModel<TInputValue, TOutputValue> ::SharkKMeansMachineLearningModel() : - m_Normalized( false ), m_K(2), m_MaximumNumberOfIterations( 10 ) + m_K(2), m_MaximumNumberOfIterations( 10 ) { // Default set HardClusteringModel this->m_ConfidenceIndex = true; @@ -77,27 +76,11 @@ SharkKMeansMachineLearningModel<TInputValue, TOutputValue> otb::Shark::ListSampleToSharkVector( this->GetInputListSample(), vector_data ); shark::Data<shark::RealVector> data = shark::createDataFromRange( vector_data ); - // Normalized input value if necessary - if( m_Normalized ) - data = NormalizeData( data ); - // Use a Hard Clustering Model for classification shark::kMeans( data, m_K, m_Centroids, m_MaximumNumberOfIterations ); m_ClusteringModel = boost::make_shared<ClusteringModelType>( &m_Centroids ); } -template<class TInputValue, class TOutputValue> -template<typename DataType> -DataType -SharkKMeansMachineLearningModel<TInputValue, TOutputValue> -::NormalizeData(const DataType &data) const -{ - shark::Normalizer<> normalizer; - shark::NormalizeComponentsUnitVariance<> normalizingTrainer( true );//zero mean - normalizingTrainer.train( normalizer, data ); - return normalizer( data ); -} - template<class TInputValue, class TOutputValue> typename SharkKMeansMachineLearningModel<TInputValue, TOutputValue> ::TargetSampleType @@ -258,6 +241,14 @@ SharkKMeansMachineLearningModel<TInputValue, TOutputValue> return true; } +template<class TInputValue, class TOutputValue> +void +SharkKMeansMachineLearningModel<TInputValue, TOutputValue> +::ExportCentroids(const std::string & filename) +{ + shark::exportCSV(m_Centroids.centroids(), filename, ' '); +} + template<class TInputValue, class TOutputValue> void SharkKMeansMachineLearningModel<TInputValue, TOutputValue>