diff --git a/README.md b/README.md index c80e3b0..408e07b 100644 --- a/README.md +++ b/README.md @@ -18,40 +18,42 @@ The `Image` class is at the basis of the development of the `ImageM` application ## Example 1 The following example performs a segmentation on a grayscale image. It uses computation of gradient, filtering, morphological processing, and management of label images. - - % read a grayscale image - img = Image.read('coins.png'); - % compute gradient as a vector image. - grad = gradient(img); - % Compute the norm of the gradient, and smooth - gradf = boxFilter(norm(grad), [5 5]); - figure; show(gradf, []); - % compute watershed after imposition of extended minima - emin = extendedMinima(gradf, 20, 4); - grad2 = imposeMinima(gradf, emin, 4); - lbl = watershed(grad2, 4); - % display binary overlay over grayscale image - show(overlay(img, lbl==0, 'g')); - % cleanup segmentation and convert to RGB image - lbl2 = killBorders(lbl); - show(label2rgb(lbl2, 'jet', 'w')); +```matlab +% read a grayscale image +img = Image.read('coins.png'); +% compute gradient as a vector image. +grad = gradient(img); +% Compute the norm of the gradient, and smooth +gradf = boxFilter(norm(grad), [5 5]); +figure; show(gradf, []); +% compute watershed after imposition of extended minima +emin = extendedMinima(gradf, 20, 4); +grad2 = imposeMinima(gradf, emin, 4); +lbl = watershed(grad2, 4); +% display binary overlay over grayscale image +show(overlay(img, lbl==0, 'g')); +% cleanup segmentation and convert to RGB image +lbl2 = killBorders(lbl); +show(label2rgb(lbl2, 'jet', 'w')); +``` ![segmentation pipeline of a grayscale image using watershed](https://github.com/mattools/matlab-image-class/blob/master/doc/images/coins-segWat.png) ## Example 2 The following example presents various ways to explore and display the content of a 3D image. - - % read data, adjust contrast, and specify spatial calibration - img = adjustDynamic(Image.read('brainMRI.hdr')); - img.Spacing = [1 1 2.5]; - % show as three orthogonal planes - figure; showOrthoPlanes(img, [60 80 13]); axis equal; - % show as three orthogonal slices in 3D - figure; showOrthoSlices(img, [60 80 13]); axis equal; view(3); - axis(physicalExtent(img)); - % display as isosurface - figure; isosurface(gaussianFilter(img, [5 5 5], 2), 50); - axis equal; axis(physicalExtent(img)); view([145 25]); light; +```matlab +% read data, adjust contrast, and specify spatial calibration +img = adjustDynamic(Image.read('brainMRI.hdr')); +img.Spacing = [1 1 2.5]; +% show as three orthogonal planes +figure; showOrthoPlanes(img, [60 80 13]); axis equal; +% show as three orthogonal slices in 3D +figure; showOrthoSlices(img, [60 80 13]); axis equal; view(3); +axis(physicalExtent(img)); +% display as isosurface +figure; isosurface(gaussianFilter(img, [5 5 5], 2), 50); +axis equal; axis(physicalExtent(img)); view([145 25]); light; +``` ![Various representations of 3D image using Image class](https://github.com/mattools/matlab-image-class/blob/master/doc/images/visu3d.png) diff --git a/demos/shape/demoCrop.m b/demos/shape/demoCrop.m new file mode 100644 index 0000000..ddf99f0 --- /dev/null +++ b/demos/shape/demoCrop.m @@ -0,0 +1,55 @@ +% Demonstration of image crop functions +% +% output = demoCrop(input) +% +% Example +% demoCrop +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2022-06-24, using Matlab 9.12.0.1884302 (R2022a) +% Copyright 2022 INRAE. + +%% Input data + +% read sample image +% (provided within the @image/sampleFiles directory) +img = Image.read('wheatGrainSlice.tif'); + +figure; show(img); + + +%% Boxes + +% need to segment image to obtain the region of the grain +seg = img > 160; +seg2 = areaOpening(killBorders(seg), 10); + +% compute boxes +box = regionBoundingBox(seg2); +obox = regionOrientedBox(seg2); + +% display boxes over image +% (requires MatGeom toolbox) +hold on; +drawBox(box, 'color', 'g', 'linewidth', 2); +drawOrientedBox(obox, 'color', 'm', 'linewidth', 2); + + +%% Crop + +resCrop = crop(img, box); +figure; show(resCrop) +title('Crop Box'); +write(resCrop, 'wheatGrainCrop.tif'); + +resCrop2 = cropOrientedBox(img, obox); +figure; show(resCrop2) +title('Crop Oriented Box'); +write(resCrop, 'wheatGrainCropOriented.tif'); + diff --git a/demos/shape/html/demoCrop.html b/demos/shape/html/demoCrop.html new file mode 100644 index 0000000..477a7c3 --- /dev/null +++ b/demos/shape/html/demoCrop.html @@ -0,0 +1,172 @@ + + + + + Codestin Search App

Contents

% Demonstration of image crop functions
+%
+%   output = demoCrop(input)
+%
+%   Example
+%   demoCrop
+%
+%   See also
+%
+
+% ------
+% Author: David Legland
+% e-mail: david.legland@inrae.fr
+% INRAE - BIA Research Unit - BIBS Platform (Nantes)
+% Created: 2022-06-24,    using Matlab 9.12.0.1884302 (R2022a)
+% Copyright 2022 INRAE.
+

Input data

% read sample image
+% (provided within the @image/sampleFiles directory)
+img = Image.read('wheatGrainSlice.tif');
+
+figure; show(img);
+

Boxes

% need to segment image to obtain the region of the grain
+seg = img > 160;
+seg2 = areaOpening(killBorders(seg), 10);
+
+% compute boxes
+box = regionBoundingBox(seg2);
+obox = regionOrientedBox(seg2);
+
+% display boxes over image
+% (requires MatGeom toolbox)
+hold on;
+drawBox(box, 'color', 'g', 'linewidth', 2);
+drawOrientedBox(obox, 'color', 'm', 'linewidth', 2);
+

Crop

resCrop = crop(img, box);
+figure; show(resCrop)
+title('Crop Box');
+write(resCrop, 'wheatGrainCrop.tif');
+
+resCrop2 = cropOrientedBox(img, obox);
+figure; show(resCrop2)
+title('Crop Oriented Box');
+write(resCrop, 'wheatGrainCropOriented.tif');
+
\ No newline at end of file diff --git a/demos/shape/html/demoCrop.png b/demos/shape/html/demoCrop.png new file mode 100644 index 0000000..d9fc51b Binary files /dev/null and b/demos/shape/html/demoCrop.png differ diff --git a/demos/shape/html/demoCrop_01.png b/demos/shape/html/demoCrop_01.png new file mode 100644 index 0000000..071ac19 Binary files /dev/null and b/demos/shape/html/demoCrop_01.png differ diff --git a/demos/shape/html/demoCrop_02.png b/demos/shape/html/demoCrop_02.png new file mode 100644 index 0000000..a6ddb06 Binary files /dev/null and b/demos/shape/html/demoCrop_02.png differ diff --git a/demos/shape/html/demoCrop_03.png b/demos/shape/html/demoCrop_03.png new file mode 100644 index 0000000..6815eef Binary files /dev/null and b/demos/shape/html/demoCrop_03.png differ diff --git a/demos/shape/html/demoCrop_04.png b/demos/shape/html/demoCrop_04.png new file mode 100644 index 0000000..2ebc27d Binary files /dev/null and b/demos/shape/html/demoCrop_04.png differ diff --git a/doc/userManual/ImageClass-Manual.lyx b/doc/userManual/ImageClass-Manual.lyx new file mode 100644 index 0000000..09d9bc6 --- /dev/null +++ b/doc/userManual/ImageClass-Manual.lyx @@ -0,0 +1,16254 @@ +#LyX 2.4 created this file. For more info see https://www.lyx.org/ +\lyxformat 620 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass scrreprt +\begin_preamble +\PassOptionsToPackage{usenames,dvipsnames}{xcolor} +\usepackage[dvipsnames]{xcolor} + +\usepackage{textcomp} + +%\usepackage[dvipsnames]{xcolor} + +%\usepackage{lstlinebgrd} + +\definecolor{bl}{rgb}{0.0,0.2,0.6} + +\definecolor{mygreen}{rgb}{0,0.6,0} +\definecolor{mygray}{rgb}{0.5,0.5,0.5} +\definecolor{lightgray}{rgb}{0.95,0.95,0.95} +\definecolor{mymauve}{rgb}{0.58,0,0.82} + +\hypersetup{colorlinks=true, citecolor=blue, linkcolor=blue} + +% package that modifies style of section headers +\addtokomafont{chapter}{\color{bl}\scshape \selectfont} +\addtokomafont{section}{\color{bl}\scshape \selectfont} +\addtokomafont{subsection}{\color{bl}\scshape \selectfont} +\addtokomafont{subsubsection}{\color{bl}\scshape \selectfont} +%\allsectionsfont{\color{bl}\scshape \selectfont } + +% setup font style for different title elements +\setkomafont{title}{\color{bl} \bfseries \scshape} +\setkomafont{author}{\centering \small} +\setkomafont{date}{\centering \small} + + +% Change the abstract environment +\usepackage[runin]{abstract} % runin option for a run-in title +\setlength\absleftindent{30pt} % left margin +\setlength\absrightindent{30pt} % right margin +\abslabeldelim{\quad } % +\setlength{\abstitleskip}{-10pt} +\renewcommand{\abstractname}{} +\renewcommand{\abstracttextfont}{\color{bl} \small \slshape } % slanted text + +% Custom headers and footers using KOMA-Script +\usepackage{scrlayer-scrpage} +\usepackage{lastpage} % for page numbering + +% Left header: chapter title on even pages +\lehead{\slshape\leftmark} +\lohead{} +% Center header: empty +\chead{} +% Right header: section title on odd pages +\rehead{} +\rohead{\slshape\rightmark} + +% Outer footer (left of even pages, right of odd pages) +\ofoot[\footnotesize page \thepage\ / \pageref{LastPage}]{\footnotesize page \thepage\ / \pageref{LastPage}} +% Center footer: empty +\cfoot{} +% Inner footer (right of even pages, left of odd pages) +\ifoot[\footnotesize \jobname]{\footnotesize \jobname} + +% left chapter no number, nothing on the right +\renewcommand{\chaptermark}[1]{\markleft{\thechapter\ #1}{}} +% right section with number +\renewcommand{\sectionmark}[1]{\markright{\thesection\ #1}} + +% Added by lyx2lyx +\usepackage[charter]{mathdesign} + +% setup of figure captions +\usepackage[format=plain,font=it,labelfont=bf]{caption} +%\captionsetup[figure]{name=Figure} +%\DeclareCaptionLabelSeparator{colon}{.} + +% Pretty display of Matlab Listings (better than mcode) +\usepackage{matlab-prettifier} + +% for mini table of contents at the beginning of chapters +\usepackage{minitoc} + +%\usepackage{makeidx} +%\makeindex +%\usepackage[totoc]{idxlayout} + +\usepackage{hyperref} + +\author{D. Legland} +\date{\today} +\end_preamble +\options idxtotoc +\use_default_options true +\maintain_unincluded_children no +\language english +\language_package default +\inputencoding auto-legacy +\fontencoding auto +\font_roman "default" "default" +\font_sans "default" "default" +\font_typewriter "default" "default" +\font_math "auto" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_roman_osf false +\font_sans_osf false +\font_typewriter_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\use_microtype false +\use_dash_ligatures true +\graphics default +\default_output_format pdf2 +\output_sync 0 +\bibtex_command default +\index_command default +\float_placement h +\float_alignment class +\paperfontsize default +\spacing single +\use_hyperref true +\pdf_bookmarks true +\pdf_bookmarksnumbered false +\pdf_bookmarksopen false +\pdf_bookmarksopenlevel 1 +\pdf_breaklinks false +\pdf_pdfborder false +\pdf_colorlinks false +\pdf_backref false +\pdf_pdfusetitle true +\papersize a4 +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine natbib +\cite_engine_type authoryear +\biblio_style plainnat +\use_bibtopic false +\use_indices true +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 0 +\use_formatted_ref 0 +\use_minted 0 +\use_lineno 0 +\index General Index +\shortcut idx +\color #008000 +\end_index +\index Image Class Methods +\shortcut met +\color #008000 +\end_index +\secnumdepth 3 +\tocdepth 1 +\paragraph_separation indent +\paragraph_indentation default +\is_math_indent 0 +\math_numbering_side default +\quotes_style swedish +\dynamic_quotes 0 +\papercolumns 1 +\papersides 2 +\paperpagestyle default +\tablestyle default +\listings_params "language=Matlab,style={Matlab-editor},numbers=left,tabsize=2,frame=single,breaklines=true,basicstyle={\scriptsize\mlttfamily},aboveskip=5pt,showspaces=false" +\tracking_changes false +\output_changes false +\change_bars false +\postpone_fragile_content false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\docbook_table_output 0 +\docbook_mathml_prefix 1 +\end_header + +\begin_body + +\begin_layout Title +An Image class for Matlab +\end_layout + +\begin_layout Abstract +Description of the +\begin_inset Quotes sld +\end_inset + +Image +\begin_inset Quotes srd +\end_inset + + class for Matlab, + that allows an intuitive manipulation of multi-dimensionnal image data, + the management of meta-data, + and provides a collection of image processing algorithms. +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +dominitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset CommandInset toc +LatexCommand tableofcontents + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Note Note +status open + +\begin_layout Plain Layout +Writing conventions: +\end_layout + +\begin_layout Itemize +function descriptions use +\begin_inset Quotes sld +\end_inset + +minisec +\begin_inset Quotes srd +\end_inset + + environment +\end_layout + +\begin_layout Itemize +functions are described by a conjugated verb (contrary to the header line of the function). +\end_layout + +\begin_layout Itemize +Descriptions end with a dot. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Chapter +Introduction +\end_layout + +\begin_layout Standard +\begin_inset Note Note +status open + +\begin_layout Plain Layout +TODO: + find a name. + CLIMP (Class for Image Processing ?) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The aim of the Image class is to facilitate the manipulation of multi-dimensional images within Matlab. + Grayscale, + color or binary images in 2D or 3D can be easily manipulated and processed. +\end_layout + +\begin_layout Standard +The following points were considered during creation of the library: +\end_layout + +\begin_layout Itemize +identify the type of image (intensity, + label, + binary, + multi-channel...) within the class, + such that display and processing functions can adapt to image type +\end_layout + +\begin_layout Itemize +keep the meta-data describing image within the class. + In particular the spatial calibration makes it possible to display images at their physical location +\end_layout + +\begin_layout Itemize +use +\begin_inset Formula $(x,y)$ +\end_inset + + indexing rather that matrix +\begin_inset Formula $(i,j)$ +\end_inset + + indexing, + facilitating the extraction of geometric primitives +\end_layout + +\begin_layout Itemize +provides several functions for displaying 3D images (orthoslices, + isosurface...) +\end_layout + +\begin_layout Itemize +provides the same function signature whatever the dimension of the image, + facilitating the generalisation of image processing workflows from 2D to 3D +\end_layout + +\begin_layout Itemize +the result of image processing functions setup the type output functions. + For example, + the result of the gradient function is a vector (multi-channels) image, + with as many channels as the image dimension +\end_layout + +\begin_layout Subsection* +Dependencies +\end_layout + +\begin_layout Itemize +The Table class from the +\begin_inset Quotes sld +\end_inset + +MatStats +\begin_inset Quotes srd +\end_inset + + library +\begin_inset Foot +status open + +\begin_layout Plain Layout +\begin_inset Flex URL +status open + +\begin_layout Plain Layout + +https://github.com/mattools/matStats +\end_layout + +\end_inset + + +\end_layout + +\end_inset + +. +\begin_inset Note Note +status open + +\begin_layout Plain Layout +functions: + fold, + unfold +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Note Note +status open + +\begin_layout Plain Layout +(the Geometry package?) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Chapter +Quick tour +\end_layout + +\begin_layout Standard +This chapter quickly presents some key features of the library. + The first script presents a typical workflow for +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +open +\end_layout + +\end_inset + + +\series bold +opening and exploring an image +\series default +. + Note the automated labeling of axes in figures. + See also Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Histogram-Color-Image" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + for representing color images. + +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% read and display image +\end_layout + +\begin_layout Plain Layout + +img = Image.read('cameraman.tif'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(img); +\end_layout + +\begin_layout Plain Layout + +% show histogram +\end_layout + +\begin_layout Plain Layout + +figure; + histogram(img); +\end_layout + +\begin_layout Plain Layout + +% plot horizontal line profile in the middle of image +\end_layout + +\begin_layout Plain Layout + +lineProfile(img, + [10 125], + [250 125]); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/overview/cameraman-show.png + lyxscale 50 + width 30text% + +\end_inset + + +\begin_inset Graphics + filename images/overview/cameraman-histogram.png + lyxscale 50 + width 30text% + +\end_inset + + +\begin_inset Graphics + filename images/overview/cameraman-lineProfile.png + lyxscale 50 + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Exploration of a grayscale image using the +\series bold +show +\series default +, + +\series bold + histogram +\series default +, + and +\series bold +lineProfile +\series default + functions. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard + +\series bold +Importing and visualizing 3D images +\series default + is almost as straightforward as for 2D images. + As or planar images, + the +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +spatial calibration +\end_layout + +\end_inset + + +\series bold +spatial calibration +\series default + can be specified (line 3), + making the 3D visualizations easier to interpret. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/brainMRI_orthoPlanes.png + lyxscale 50 + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +showOrthoPlanes +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/brainMRI_orthoSlices3d.png + lyxscale 50 + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +showOrthoSlices3d +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/brainMRI_isosurface3d.png + lyxscale 50 + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +isosurface +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Visualization-3D-Image-1" + +\end_inset + +Several representations of a 3D grayscale image. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% read data, + adjust contrast, + and specify spatial calibration +\end_layout + +\begin_layout Plain Layout + +img = adjustDynamic(Image.read('brainMRI.hdr')); +\end_layout + +\begin_layout Plain Layout + +img.Spacing = [1 1 2.5]; +\end_layout + +\begin_layout Plain Layout + +% show as three orthogonal planes +\end_layout + +\begin_layout Plain Layout + +figure; + showOrthoPlanes(img, + [60 80 13]); + axis equal; +\end_layout + +\begin_layout Plain Layout + +% show as three orthogonal slices in 3D +\end_layout + +\begin_layout Plain Layout + +figure; + showOrthoSlices(img, + [60 80 13]); + axis equal; + view(3); + +\end_layout + +\begin_layout Plain Layout + +axis(physicalExtent(img)); +\end_layout + +\begin_layout Plain Layout + +% display as isosurface +\end_layout + +\begin_layout Plain Layout + +figure; + isosurface(gaussianFilter(img, + [5 5 5], + 2), + 50); +\end_layout + +\begin_layout Plain Layout + +axis equal; + axis(physicalExtent(img)); + view([145 25]); + light; + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The following script provides an example of +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +segmentation +\end_layout + +\end_inset + + +\series bold + segmentation using watershed algorithm +\series default +. + It also demonstrates the use of gradient +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + image (see Page +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:gradient" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +), + of binary image overlay +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +overlay +\end_layout + +\end_inset + + (see Page +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:overlay" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +), + and conversion from label +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +label image +\end_layout + +\end_inset + + to color image (see Page +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:label2rgb" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('coins.png'); +\end_layout + +\begin_layout Plain Layout + +% compute gradient as a vector image. + +\end_layout + +\begin_layout Plain Layout + +grad = gradient(img); +\end_layout + +\begin_layout Plain Layout + +% Compute norm, + and smooth +\end_layout + +\begin_layout Plain Layout + +gradf = boxFilter(norm(grad), + [5 5]); +\end_layout + +\begin_layout Plain Layout + +figure; + show(gradf, + []); +\end_layout + +\begin_layout Plain Layout + +% compute watershed after imposition of extended minima +\end_layout + +\begin_layout Plain Layout + +emin = extendedMinima(gradf, + 20, + 4); +\end_layout + +\begin_layout Plain Layout + +grad2 = imposeMinima(gradf, + emin, + 4); +\end_layout + +\begin_layout Plain Layout + +lbl = watershed(grad2, + 4); +\end_layout + +\begin_layout Plain Layout + +% display binary overlay over grayscale image +\end_layout + +\begin_layout Plain Layout + +show(overlay(img, + lbl==0, + 'g')); +\end_layout + +\begin_layout Plain Layout + +% cleanup segmentation and convert to RGB image +\end_layout + +\begin_layout Plain Layout + +lbl2 = killBorders(lbl); +\end_layout + +\begin_layout Plain Layout + +show(label2rgb(lbl2, + 'jet', + 'w')); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/overview/coins-grad-filt.png + width 30text% + +\end_inset + + +\begin_inset Graphics + filename images/overview/coins-segWat-ovr.png + width 30text% + +\end_inset + + +\begin_inset Graphics + filename images/overview/coins-segWat-rgb.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Segmentation of a grayscale image using watershed. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Once a label image is generated, + several region properties can be analyzed (see also section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Region-Analysis" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + The following lines add display of region centroids on current figure: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +pts = centroid(lbl2); + +\end_layout + +\begin_layout Plain Layout + +hold on; + plot(pts(:,1), + pts(:,2), + 'k*'); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +If spatial calibration is specified, + it is usually taken into account for measurements. +\end_layout + +\begin_layout Chapter +Image class overview +\end_layout + +\begin_layout Standard +The image class stores image data together with various meta-data. + Several image processing operators are provided as Image class methods. + When possible and appropriate, + the meta-data associated to an image are kept in the resulting image. +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Indexing and dimensions +\end_layout + +\begin_layout Standard +The data are stored as a 5-dimensional array. + The dimensions correspond to the X, + Y, + Z, + channel, + and frame axes, + in that order. + In particular, + grayscale or intensity images are stored with dimensions 1 and 2 permuted compared to Matlab representation. +\end_layout + +\begin_layout Subsection +Coordinate system +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +coordinate system +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Elements of a two-dimensional image are indexed in the +\begin_inset Formula $x$ +\end_inset + +, + +\begin_inset Formula $y$ +\end_inset + + order. + Three-dimensional images are indexed in the +\begin_inset Formula $(x,y,z)$ +\end_inset + + order. + Indexing is +\begin_inset Formula $1$ +\end_inset + +-based, + following Matlab convention for arrays (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Image-Coordinate-System" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/calib/image_coordinate_system.png + width 60text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Image-Coordinate-System" + +\end_inset + +Coordinate system of a two-dimensional image. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +An element of an image is usually associated to a square region centered on its coordinates. + Its bounds are therefore +\begin_inset Formula $\pm0.5$ +\end_inset + + around each dimension. + The spatial calibration of the image can be used to relate index coordinates to physical space coordinates (see Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Spatial-Calibration" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Standard +Please be aware that spatial calibration stored in image files sometimes refers to the printing properties. + It is therefore good practice to check the spatial calibration of images before performing any measurements. +\end_layout + +\begin_layout Subsection +Three-dimensional images +\end_layout + +\begin_layout Standard +Most operators of the Image class can be applied indifferently on 2D or 3D images. + For instance, + computing the histogram of a 2D or 3D image relies on the same command. + A script operating on 2D images can therefore easily by applied to a 3D image. + +\end_layout + +\begin_layout Standard +For 3D images, + several visualisation functions are presented in more details in section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Display-3D-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Section +Image meta-data +\end_layout + +\begin_layout Standard +Several metadata stored as properties in the Image class allow to facilitate the interpretation of the image. +\end_layout + +\begin_layout Subsection +Image Type +\end_layout + +\begin_layout Standard +The +\begin_inset Quotes sld +\end_inset + +Type +\begin_inset Quotes srd +\end_inset + + property is used to identify the type of information within the image. + Common values are +\begin_inset Quotes sld +\end_inset + +grayscale +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +binary +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +label +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +color +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +vector +\begin_inset Quotes srd +\end_inset + +. + The type of image is used to choose relevant options for some operators, + or to automatically setup the display of images. +\end_layout + +\begin_layout Standard +Most methods in the Image class attempts to automatically determine the type of the result. + For example, + segmentation methods results in images with +\begin_inset Quotes sld +\end_inset + +label +\begin_inset Quotes srd +\end_inset + + or +\begin_inset Quotes sld +\end_inset + +binary +\begin_inset Quotes srd +\end_inset + + type. + +\end_layout + +\begin_layout Subsection +Spatial calibration +\end_layout + +\begin_layout Standard +The spatial calibration +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +spatial calibration +\end_layout + +\end_inset + + of images can be specified through the properties +\begin_inset Quotes sld +\end_inset + +Spacing +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +Origin +\begin_inset Quotes srd +\end_inset + +, + and +\begin_inset Quotes sld +\end_inset + +UnitName +\begin_inset Quotes srd +\end_inset + +. + Spacing and Origin are given as +\begin_inset Formula $1\times n_{d}$ +\end_inset + + row vectors, + where +\begin_inset Formula $n_{d}$ +\end_inset + + is the number of spatial dimensions (usually 2 or 3). + Spacing corresponds to the space between two consecutive elements. + It also corresponds to the size of each element. + Origin corresponds to the coordinates of the center of the first element. + The property +\begin_inset Quotes sld +\end_inset + +UnitName +\begin_inset Quotes srd +\end_inset + + is either empty or a char array. + It is common to all the spatial dimensions. +\end_layout + +\begin_layout Standard +Section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Spatial-Calibration" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + provides more information about functions dedicated to conversions between array coordinates and spatial coordinates. +\end_layout + +\begin_layout Subsection +Multi-channels images +\end_layout + +\begin_layout Standard +The image class natively manages multi-channel images. + This includes color images, + gradient images, + or complex images. + +\end_layout + +\begin_layout Standard +Multi-channel images store the name of each channel in the +\begin_inset Quotes sld +\end_inset + +ChannelNames +\begin_inset Quotes srd +\end_inset + + property. + This property is propagated when operators are applied on multi-channel images. +\end_layout + +\begin_layout Standard +Section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Multi-Channel-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + describes more operators dedicated to multi-channel images. + Some color conversion operators are also provided (section +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Color-Conversions" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Subsection +Time calibration +\end_layout + +\begin_layout Standard +Movies or time-lapse images consider the time axis as the fifth dimension of the data array. + +\end_layout + +\begin_layout Standard +The +\begin_inset Quotes sld +\end_inset + +TimeStep +\begin_inset Quotes srd +\end_inset + + and the +\begin_inset Quotes sld +\end_inset + +TimeUnit +\begin_inset Quotes srd +\end_inset + + properties allow to specifiy the calibration of the time axis. +\end_layout + +\begin_layout Standard +Section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Time-Lapse-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + describes more operators dedicated to time-lapse images. + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Image class properties +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Image-Class-Properties" + +\end_inset + + +\end_layout + +\begin_layout Standard +The properties of the Image class are summarized here: +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +Data the inner data of the image, + as a 5D array indexing in [x,y,z,c,t] order. + +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +Type the type of image (grayscale, + color, + complex...), + represented as a string from a predefined set of values. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +Name the name of the image. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +FilePath the path to the file containing the image, + if relevant. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +Origin spatial origin of the image, + corresponding to the position of the (upper-) top-left pixel. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +Spacing the amount of space between two pixels or voxels. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +UnitName the name of the unit for spatial calibration. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +ChannelNames the names associated to each channel. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +TimeStep for a time-lapse image, + the interval between two frames. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +TimeUnit the unit of the time step (can be +\begin_inset Quotes sld +\end_inset + +s +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +mn +\begin_inset Quotes srd +\end_inset + +, + +\begin_inset Quotes sld +\end_inset + +h +\begin_inset Quotes srd +\end_inset + +....) +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +\begin_inset Note Note +status open + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +AxisNames the name of each of the axes. +\end_layout + +\begin_layout Labeling +\labelwidthstring 00.00.0000 +Calibration the information about the calibration (spatial calibration, + channel names...) of the image. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Utility methods: +\end_layout + +\begin_layout Minisec +getBuffer +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +getBuffer +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +Returns +\family default +\series default +\shape default +\size default +\emph default +\nospellcheck default +\bar default +\strikeout default +\xout default +\uuline default +\uwave default +\noun default +\color inherit + the inner data buffer of an image, + with dimensions permuted such that they correspond to Matlab's [y,x,c,z,t] order. +\end_layout + +\begin_layout Chapter +Creation and basic manipulation +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Creation +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Image-Creation" + +\end_inset + + +\end_layout + +\begin_layout Subsection +Creation from a Matlab array +\end_layout + +\begin_layout Standard +A simple way for initializing an Image is to create an instance from a classical Matlab array containing image data: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> data = imread('rice.png'); + +\end_layout + +\begin_layout Plain Layout + +>> img = Image(img); +\end_layout + +\begin_layout Plain Layout + +>> whos img +\end_layout + +\begin_layout Plain Layout + + Name Size Bytes Class Attributes +\end_layout + +\begin_layout Plain Layout + + img 256x256 8 grayscale Image +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The type of image and the image dimensions are automatically inferred from the input array. + The size of the Image instance correspond to the dimensions along the physical dimensions and does not take into account channel number. + +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> data = imread('peppers.png'); +\end_layout + +\begin_layout Plain Layout + +>> img = Image(data); +\end_layout + +\begin_layout Plain Layout + +>> whos data img +\end_layout + +\begin_layout Plain Layout + + Name Size Bytes Class Attributes +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + + data 384x512x3 589824 uint8 +\end_layout + +\begin_layout Plain Layout + + img 512x384 8 color Image +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Create new images +\end_layout + +\begin_layout Standard +Several static methods are provided by the Image class for the creating new empty images. + +\end_layout + +\begin_layout Minisec +create +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +create +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Creates a new image instance by choosing the size and the type of the data array. + Example: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.create([200 200], + 'uint8'); +\end_layout + +\begin_layout Plain Layout + +>> whos img +\end_layout + +\begin_layout Plain Layout + + Name Size Bytes Class Attributes +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + + img 200x200 8 grayscale Image +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Other static methods are provided to initialize with a given value and image type: +\end_layout + +\begin_layout Minisec +ones +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +ones +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Creates a new image containing only ones. + Usage: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.ones(dims); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +zeros +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +zeros +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Creates a new image containing only zeros values. + Usage: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.zeros(dims); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +false +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +false +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Creates a new image of type 'binary' containing only 'false' values. + Usage: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.false(dims); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +true +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +true +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Creates a new image of type 'binary' containing only 'true' values. + Usage: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.true(dims); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Color images +\end_layout + +\begin_layout Minisec +img = Image.createRGB(red, + green, + blue); +\end_layout + +\begin_layout Standard +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +createRGB +\end_layout + +\end_inset + +Creates a new RGB image by concatenating three arrays corresponding to the red, + green and blue channels. + Usage: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.createRGB(red, + green, + blue); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Read and write +\end_layout + +\begin_layout Subsection +Reading from files +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Read" + +\end_inset + + +\end_layout + +\begin_layout Standard +Most images files can be read using the static +\begin_inset Quotes sld +\end_inset + +read +\begin_inset Quotes srd +\end_inset + + function. + Images with more than two dimensions are often provided as series of images. + They can be imported using the +\begin_inset Quotes sld +\end_inset + +readSeries +\begin_inset Quotes srd +\end_inset + + function. +\end_layout + +\begin_layout Minisec +read +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +read +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Reads an image from a file. + The classical formats (TIFF, + PNG, + JPG...) are supported, + as well as some medical image formats (Analyze, + Dicom), + and the MetaImage format from KitWare +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout +\begin_inset Flex URL +status open + +\begin_layout Plain Layout + +https://itk.org/Wiki/ITK/MetaIO/Documentation +\end_layout + +\end_inset + + +\end_layout + +\end_inset + +. + Movie file formats supported by Matlab can also be read. + +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout +Add a table of supported formats and where is the reader +\end_layout + +\end_inset + +Example: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('cameraman.tif'); + % read a gray scale image +\end_layout + +\begin_layout Plain Layout + +>> rgb = Image.read('peppers.png'); + % read a color image +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +readSeries +\end_layout + +\begin_layout Standard +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +readSeries +\end_layout + +\end_inset + +Reads a series of (2D) image from a file, + and returns the resulting 3D image. + Example: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.readSeries('files/img*.tif'); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +importRaw +\end_layout + +\begin_layout Standard +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +importRaw +\end_layout + +\end_inset + +Import image from a binary data file, + by specifying image size, + data type, + and optionally byte order. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.importRaw('binaryFile.raw', + [200 200 100], + 'uint8'); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Writing to files +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Write" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +write +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The content of an image can be saved to a file by using the +\begin_inset Quotes sld +\end_inset + +write +\begin_inset Quotes srd +\end_inset + + function. +\end_layout + +\begin_layout Minisec +write +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +write +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Writes the content of an image into a file. + Example: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> write(img, + 'newFile.tif'); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +3D images can be saved into a multi-page TIFF file. + Example: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('brainMRI.hdr'); +\end_layout + +\begin_layout Plain Layout + +>> write(img, + 'img3d.tif'); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Getting meta-data from file +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:FileInfo" + +\end_inset + + +\end_layout + +\begin_layout Minisec +fileInfo +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +fileInfo +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the meta-data stored in the specified image file. + Usage: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +info = Image.fileInfo(fileName); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Image information +\end_layout + +\begin_layout Standard +Image class provides functions for assessing the size and the dimension of the array, + the size and the type of its elements (pixels or voxels), + as well as some functions for extraction scalar or color values. + +\end_layout + +\begin_layout Subsection +Size and dimensions +\end_layout + +\begin_layout Standard +Several functions are provided for accessing the size and the dimensionality of the image. +\end_layout + +\begin_layout Minisec +size +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +size +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the size of the image, + restricted to the space dimensions, + as a 1-by-2 or 1-by-3 array. + If an additional parameter +\begin_inset Formula $d$ +\end_inset + + is specified, + only the requested dimension is returned. + If +\begin_inset Formula $d=4$ +\end_inset + +, + it returns the channel number. + It +\begin_inset Formula $d=5$ +\end_inset + +, + it returns frame number. + Example: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('peppers.png'); + % read a color image +\end_layout + +\begin_layout Plain Layout + +>> size(img) % display the 2D size +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + 512 384 +\end_layout + +\begin_layout Plain Layout + +>> size(img, + 4) % display the number of channels +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + 3 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +ndims +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ndims +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the number of spatial dimensions of the image. +\end_layout + +\begin_layout Minisec +elementCount +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +elementCount +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the number of elements in the image, + equal to the product of the spatial dimensions. +\end_layout + +\begin_layout Minisec +isempty +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isempty +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image has no element (element number is zero). +\end_layout + +\begin_layout Minisec +isPlanarImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isPlanarImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image has two spatial dimensions. +\end_layout + +\begin_layout Minisec +is3dImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +is3dImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image has three spatial dimensions. +\end_layout + +\begin_layout Minisec +isTimeLapseImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isTimeLapseImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if image size in the fifth dimension is greater than 1. +\end_layout + +\begin_layout Subsection +Element type +\end_layout + +\begin_layout Standard +Several functions give information about the type of data stored within image. +\end_layout + +\begin_layout Minisec +class +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +class +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns a char array describing the type of image, + such as 'color image' (used for display in editor). + +\end_layout + +\begin_layout Minisec +elementSize +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +elementSize +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the size of an image element. + A pixel of an RGB image has size equal to 3-by-1. + A pixel of a color movie has size equal to 3-by-nFrames. +\end_layout + +\begin_layout Minisec +isBinaryImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isBinaryImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image is binary (see section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Binary-images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +isColorImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isColorImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image is color. +\end_layout + +\begin_layout Minisec +isComplexImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isComplexImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image contains complex values. +\end_layout + +\begin_layout Minisec +isGrayscaleImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isGrayscaleImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image is grayscale. +\end_layout + +\begin_layout Minisec +isIntensityImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isIntensityImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image is intensity, + i.e. + contains scalar values that should be interpreted as floating point values. +\end_layout + +\begin_layout Minisec +isLabelImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isLabelImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image is label or binary (see section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Label-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +isScalarImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isScalarImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image contains scalar values. +\end_layout + +\begin_layout Minisec +isVectorImage +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isVectorImage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if the image contains vector values, + i.e. + is a multi-channel image. +\end_layout + +\begin_layout Section +Spatial calibration +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Spatial-Calibration" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +spatial calibration +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The spatial calibration of an image can be obtained from the properties +\series bold +Origin +\series default +, + +\series bold +Spacing +\series default +, + and +\series bold +UnitName +\series default + (see section +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Image-Class-Properties" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + The field +\series bold +Calibrated +\series default + is set to true if one of the calibration properties was modified. + When possible, + the spatial calibration is kept through the different operators applied on image. + This allows to compare and display within the same basis images with different view ports. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('peppers.png'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(img); + hold on; +\end_layout + +\begin_layout Plain Layout + +img2 = crop(img, + [71 240 181 330]); +\end_layout + +\begin_layout Plain Layout + +hold on; + show(invert(img2)); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/calib/demoCrop.png + width 60text% + +\end_inset + + +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Calibration-Demo-Crop" + +\end_inset + +Cropping an image keeps the spatial calibration +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Additional methods provide facilities for converting between pixel coordinates and world or user coordinates. +\end_layout + +\begin_layout Minisec +physicalSize +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +physicalSize +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the physical size occupied by this image, + as the product of image dimensions by the corresponding spatial calibration. +\end_layout + +\begin_layout Minisec +physicalExtent +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +physicalExtent +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the physical size occupied by this image, + as a box. +\end_layout + +\begin_layout Minisec +isCalibrated +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isCalibrated +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns true if image is calibrated, + i.e. + one of origin, + spacing or unit is different from default. +\end_layout + +\begin_layout Minisec +clearCalibration +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +clearCalibration +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Resets the spatial calibration of image to default values (spacing and origin equal to 1 along each direction). +\end_layout + +\begin_layout Minisec +copySpatialCalibration +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +copySpatialCalibration +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Copies the spatial calibration from an image to another. +\end_layout + +\begin_layout Minisec +indexToPoint +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +indexToPoint +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image coordinates to real-world coordinates. +\end_layout + +\begin_layout Minisec +pointToIndex +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +pointToIndex +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts real world coordinates to integer image coordinates. +\end_layout + +\begin_layout Minisec +pointToContinuousIndex +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +pointToContinuousIndex +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts real world coordinates to image coordinates, + keeping the non integer part. +\end_layout + +\begin_layout Minisec +xData +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +xData +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns a row vector corresponding to the x-coordinates of pixels. +\end_layout + +\begin_layout Minisec +yData +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +yData +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns a row vector corresponding to the y-coordinates of pixels. +\end_layout + +\begin_layout Minisec +zData +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +zData +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns a row vector corresponding to the z-coordinates of pixels. +\end_layout + +\begin_layout Chapter +Visualization and exploration +\end_layout + +\begin_layout Standard +Several methods are proposed to facilitate the visualization of images of various dimensionalities and content types. + Most Image methods try to provide unified syntax whatever the dimensionality and the type of the image. +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Visualisation of 2D images +\end_layout + +\begin_layout Minisec +show +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +show +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays an image on the current figure. + If the images is calibrated, + the spatial calibration is kept into account for display. +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% read and display image +\end_layout + +\begin_layout Plain Layout + +img = Image.read('cameraman.tif'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(img); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Histogram and line profile +\end_layout + +\begin_layout Standard +The following scripts presents a typical workflow for opening and exploring an image. + When possible, + title of axes is initialized from image name. + +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% read and display image +\end_layout + +\begin_layout Plain Layout + +img = Image.read('cameraman.tif'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(img); +\end_layout + +\begin_layout Plain Layout + +% show histogram +\end_layout + +\begin_layout Plain Layout + +figure; + histogram(img); +\end_layout + +\begin_layout Plain Layout + +% plot horizontal line profile in the middle of image +\end_layout + +\begin_layout Plain Layout + +lineProfile(img, + [10 125], + [250 125]); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/cameraman.png + width 25text% + +\end_inset + + +\begin_inset Graphics + filename images/overview/cameraman-histogram.png + lyxscale 50 + width 30text% + +\end_inset + + +\begin_inset Graphics + filename images/overview/cameraman-lineProfile.png + lyxscale 50 + width 35text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Histogram-LineProfile" + +\end_inset + +Exploration of a grayscale image (cameraman) using the +\series bold + histogram +\series default +, + and +\series bold +lineProfile +\series default + functions. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Histogram +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +histogram +\end_layout + +\end_inset + + and line profile +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +line profile +\end_layout + +\end_inset + + can be computed on a color image using the same syntax, + as illustrated on the following script, + and the Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Histogram-Color-Image" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% read color image and display it +\end_layout + +\begin_layout Plain Layout + +img = Image.read('peppers.png'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(img); +\end_layout + +\begin_layout Plain Layout + +% Compute histogram +\end_layout + +\begin_layout Plain Layout + +figure; + histogram(img); +\end_layout + +\begin_layout Plain Layout + +% Compute line profile +\end_layout + +\begin_layout Plain Layout + +lineProfile(img, + [150 50], + [150 300]); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/peppers-lineOverlay.png + width 30text% + +\end_inset + + +\begin_inset Graphics + filename images/vizu/peppers-histogram.png + lyxscale 50 + width 35text% + +\end_inset + + +\begin_inset Graphics + filename images/vizu/peppers-lineProfile.png + lyxscale 50 + width 35text% + +\end_inset + + +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Histogram-Color-Image" + +\end_inset + +Histogram and line profile computed on a color image +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:histogram" + +\end_inset + +histogram +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +histogram +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays the histogram of the gray values or the color histogram of a 2D or 3D image. +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:lineProfile" + +\end_inset + +lineProfile +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +lineProfile +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Plots a line profile on the current image. +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:interp" + +\end_inset + +interp +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +interp +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Interpolates +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +interpolation +\end_layout + +\end_inset + + the image at the specified point(s), + and returns the corresponding value(s). + Returns the values into floating point arrays with the same size as the array of sampling coordinates. +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:jointHistogram" + +\end_inset + +jointHistogram +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +jointHistogram +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the joint histogram +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +joint histogram +\end_layout + +\end_inset + + of two images containing scalar values. + If not specified, + the bins are computed automatically from the range of image intensities (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Joint_Histogram" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\begin_inset Note Note +status open + +\begin_layout Plain Layout +example with registered images ? +\end_layout + +\end_inset + + +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('peppers.png'); +\end_layout + +\begin_layout Plain Layout + +>> histoRG = jointHistogram(channel(img, + 1), + channel(img, + 2)); +\end_layout + +\begin_layout Plain Layout + +>> histoRG2 = log(histoRG+1); +\end_layout + +\begin_layout Plain Layout + +>> figure; + show(log(histoRG+1)); + colormap(jet); + set(gca, + 'ydir', + normal); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/peppers_JointHistogramRG_log_annotated.png + width 50text% + +\end_inset + + +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Joint_Histogram" + +\end_inset + +Joint histogram of two channels of a color image. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Visualization of 3D images +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Display-3D-Images" + +\end_inset + + +\end_layout + +\begin_layout Standard +Several functions allow to visually represent 3D images. + The spatial calibration is kept into account for display. + An example of result is presented on Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Visualization-3D-Image" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Minisec +showOrthoPlanes +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +showOrthoPlanes +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays three mutually orthogonal planes +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +orthogonal planes +\end_layout + +\end_inset + + (see Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "fig:Visualization-3D-Image" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +showOrthoSlices +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +showOrthoSlices +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays three mutually orthogonal planes in 3D +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +orthogonal slices +\end_layout + +\end_inset + + (see Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "fig:Visualization-3D-Image" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +showSlice3d +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +showSlice3d +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays a 3D slice +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +slice 3d +\end_layout + +\end_inset + + of a 3D image. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/brainMRI_orthoPlanes.png + lyxscale 50 + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +showOrthoPlanes +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/brainMRI_orthoSlices3d.png + lyxscale 50 + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +showOrthoSlices3d +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/vizu/brainMRI_isosurface3d.png + lyxscale 50 + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +isosurface +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Visualization-3D-Image" + +\end_inset + +Different representations of a 3D grayscale image. + Results of the showOrthoPlanes, + showOrthoSlices3d and isosurface functions. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +showXSlice +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +showXSlice +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays a YZ slice of a 3D image. +\end_layout + +\begin_layout Minisec +showYSlice +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +showYSlice +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays a ZY slice of a 3D image. +\end_layout + +\begin_layout Minisec +showZSlice +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +showZSlice +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays a XY slice of a 3D image. +\end_layout + +\begin_layout Minisec +isosurface +\begin_inset CommandInset label +LatexCommand label +name "op:isosurface" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isosurface +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the isosurface +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isosurface +\end_layout + +\end_inset + + within a 3D grayscale or intensity image. + This is mainly a wrapper to the native isosurface function. + See also the binarization operators (Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Binarization" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +) and the regionIsosurface method (section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "sec:Region-Analysis" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout +do we have isocontour somewhere? +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Value range +\end_layout + +\begin_layout Standard +Several functions allow to quickly assess the range of values within an image. +\end_layout + +\begin_layout Minisec +dataExtent +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +dataExtent +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the minimal and maximal intensity values within the image. +\end_layout + +\begin_layout Minisec +grayscaleExtent +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +grayscaleExtent +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the minimal and maximal values of image data type +\begin_inset Note Note +status open + +\begin_layout Plain Layout +rename as +\begin_inset Quotes sld +\end_inset + +dataTypeExtent +\begin_inset Quotes srd +\end_inset + + ? +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Standard +Exemple: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('cameraman.tif'); +\end_layout + +\begin_layout Plain Layout + +>> dataExtent(img) +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + 7 253 +\end_layout + +\begin_layout Plain Layout + +>> grayscaleExtent(img) +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + 0 255 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Summary statistics +\end_layout + +\begin_layout Standard +Some methods also provide summary statistics about gray values within an image. + Contrary to native behavior on Matlab arrays, + the overloaded functions return a scalar value whatever the dimension of the input image. +\end_layout + +\begin_layout Standard +Example: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('cameraman.tif'); +\end_layout + +\begin_layout Plain Layout + +>> [mean(img) std(img) max(img)] +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + 119 62 253 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Note Note +status open + +\begin_layout Plain Layout +For color images, + the result may be provided as a 1-by-3 row vector. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +mean +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +mean +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the mean value within image (as a scalar). +\end_layout + +\begin_layout Minisec +median +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +median +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the median of values within image. +\end_layout + +\begin_layout Minisec +sum +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +sum +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the sum of values within image. +\end_layout + +\begin_layout Minisec +std +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +std +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the standard deviation of values within image. +\end_layout + +\begin_layout Minisec +var +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +var +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the variance of values within image. +\end_layout + +\begin_layout Minisec +min +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +min +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the minimum of the values within the image. + Can also be used to compute the minimum image between two images (Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Binary-Operators" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +max +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +max +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the maximum of the values within the image. + Can also be used to compute the maximum image between two images (Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Binary-Operators" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Chapter +Array manipulation +\end_layout + +\begin_layout Standard +Several methods are provided to facilitate the manipulation of Image instances in the same way as a classical Matlab array. + When possible, + the spatial calibration of the input image(s) is kept in the output image(s). + The image type (binary, + label...) is also inferred automatically depending both on the type of input image and the type of processing. +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Slicing and indexing +\end_layout + +\begin_layout Standard +Image class uses indexing in the x-y order for 2D images, + and in the x-y-z order for 3D images. + +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> % read sample image +\end_layout + +\begin_layout Plain Layout + +>> img = Image.read('rice.png'); +\end_layout + +\begin_layout Plain Layout + +>> % display values int the top-left corner +\end_layout + +\begin_layout Plain Layout + +>> img(1:5, + 1:3) +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + 3x5 uint8 matrix +\end_layout + +\begin_layout Plain Layout + + 122 92 95 99 102 +\end_layout + +\begin_layout Plain Layout + + 99 99 102 82 100 +\end_layout + +\begin_layout Plain Layout + + 97 107 103 86 98 +\end_layout + +\begin_layout Plain Layout + +>> % get value of pixel with x=3 and y=2 +\end_layout + +\begin_layout Plain Layout + +>> img(3,2) +\end_layout + +\begin_layout Plain Layout + +ans = +\end_layout + +\begin_layout Plain Layout + + uint8 +\end_layout + +\begin_layout Plain Layout + + 102 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Note that the returned value is 102, + not 107 as it would be the case if the variable +\begin_inset Quotes sld +\end_inset + +img +\begin_inset Quotes srd +\end_inset + + was a matrix. +\end_layout + +\begin_layout Section +Shape operations +\end_layout + +\begin_layout Standard +The following functions change the shape of the image arrays, + usually without interpolation. +\end_layout + +\begin_layout Subsection +Flip, + slice, + rotate... +\end_layout + +\begin_layout Standard +Some examples are presented on Figure +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Geometric-Operations" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/cameraman-flip.png + width 23text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +flip +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/cameraman-rot90.png + width 23text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +rotate90 +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/cameraman-rot30.png + lyxscale 71 + width 23text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +rotate +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/cameraman-circshift.png + width 23text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +circshift +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Geometric-Operations" + +\end_inset + +Examples of geometric operations on images. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +flip +\begin_inset CommandInset label +LatexCommand label +name "op:flip" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +flip +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Flips the image about the specified dimension (See Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Geometric-Operations" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-a). +\end_layout + +\begin_layout Minisec +permute +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +permute +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Permutes image dimensions. +\end_layout + +\begin_layout Minisec +rotate90 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +rotate90 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Rotates the (2D) image by 90 degrees (See Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Geometric-Operations" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-b). +\end_layout + +\begin_layout Minisec +rotate +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +rotate +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Rotates the (2D) image by an angle specified in degrees (See Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Geometric-Operations" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-c). +\end_layout + +\begin_layout Minisec +slice +\begin_inset CommandInset label +LatexCommand label +name "op:slice" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +slice +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +slice +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the specified XY slice from a 3D image. + The result is given as 2D image. +\end_layout + +\begin_layout Minisec +slice3d +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +slice3d +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +slice 3d +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the specified slice from a 3D image, + by choosing slicing axis. + The result is given as a 3D image, + with one of the spatial dimensions equal to 1. +\end_layout + +\begin_layout Minisec +montage +\begin_inset CommandInset label +LatexCommand label +name "op:montage" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +montage +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Displays multiple image frames as rectangular montage. +\end_layout + +\begin_layout Minisec +squeeze +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +squeeze +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Removes singleton dimensions from input image. +\end_layout + +\begin_layout Minisec +reshape +\begin_inset CommandInset label +LatexCommand label +name "op:reshape" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +reshape +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Reorders the elements of the image into a new image with different dimensions. +\end_layout + +\begin_layout Subsection +Crop +\end_layout + +\begin_layout Standard +Extract a rectangular region from the image. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% read demo image +\end_layout + +\begin_layout Plain Layout + +img = Image.read('wheatGrainSlice.tif'); +\end_layout + +\begin_layout Plain Layout + +% create segmentation +\end_layout + +\begin_layout Plain Layout + +seg = areaOpening(killBorders(seg > 160), + 10); +\end_layout + +\begin_layout Plain Layout + +% crop with bounding box +\end_layout + +\begin_layout Plain Layout + +box = regionBoundingBox(seg); +\end_layout + +\begin_layout Plain Layout + +resCrop = crop(img, + box); +\end_layout + +\begin_layout Plain Layout + +% crop with oriented bounding box +\end_layout + +\begin_layout Plain Layout + +obox = regionOrientedBox(seg); +\end_layout + +\begin_layout Plain Layout + +resCrop2 = cropOrientedBox(img, + obox); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +crop +\begin_inset CommandInset label +LatexCommand label +name "op:crop" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +crop +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +crop +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Crops the rectangular region from a 2D or 3D image, + and returns a new image (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "fig:Crop-Ops" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-b). +\end_layout + +\begin_layout Minisec +cropOrientedBox +\begin_inset CommandInset label +LatexCommand label +name "op:cropOrientedBox" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +cropOrientedBox +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +oriented box +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Crops the content of an image within an oriented box (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "fig:Crop-Ops" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-c). + See also the cropLabel function (p. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:cropLabel" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/wheatGrainCrop-boxes.tif.png + width 24text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Original Image +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/wheatGrainCrop.tif + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Crop Box +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/wheatGrainCropOriented.tif + lyxscale 71 + width 33text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Crop Oriented Box +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Crop-Ops" + +\end_inset + +Cropping an image. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard + +\end_layout + +\begin_layout Subsection +Concatenation +\end_layout + +\begin_layout Standard +Images can be concatenated the same way as classical Matlab arrays. + Image concatenation results in new image instances. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> % read sample image +\end_layout + +\begin_layout Plain Layout + +>> img = Image.read('cameraman.tif'); +\end_layout + +\begin_layout Plain Layout + +>> % concatenate in both X and Y directions +\end_layout + +\begin_layout Plain Layout + +>> img2 = [img img img ; + img img img]; +\end_layout + +\begin_layout Plain Layout + +>> % (equivalent to +\begin_inset Quotes sld +\end_inset + +repmat(img, + [3 2]) +\begin_inset Quotes srd +\end_inset + +) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/shape/cameraman-concat.png + lyxscale 70 + width 50text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Concatenation" + +\end_inset + +Concatenation of images. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +[] +\end_layout + +\begin_layout Standard +Overloads the array concatenation +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +concatenation +\end_layout + +\end_inset + + operator. +\end_layout + +\begin_layout Minisec +cat +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +cat +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Concatenates two images in the specified dimension. +\end_layout + +\begin_layout Minisec +horzcat +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +horzcat +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Concatenates two images in the horizontal (x) dimension. +\end_layout + +\begin_layout Minisec +vercat +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +vercat +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Concatenates two images in the vertical (y) dimension. +\end_layout + +\begin_layout Minisec +repmat +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +repmat +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Creates a new image obtained by repeating the input image. +\end_layout + +\begin_layout Subsection +Other shape operations +\end_layout + +\begin_layout Minisec +resize +\begin_inset Index met +range none +pageformat default +status open + +\begin_layout Plain Layout +resize +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +resize +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Resizes the input image. +\end_layout + +\begin_layout Minisec +subsample +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +subsample +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Resamples the input image by integer ratio. +\end_layout + +\begin_layout Minisec +resample +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +resample +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Resamples the input image using a new coordinate basis. +\end_layout + +\begin_layout Minisec +circshift +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +circshift +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Overloads the circshift operator (See Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Geometric-Operations" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-d). + Can be useful for complex images resulting from fft, + see section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Complex-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Minisec +ctranspose +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ctranspose +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Overloads the transpose operator. +\end_layout + +\begin_layout Section +Arithmetic operations +\end_layout + +\begin_layout Standard +Most arithmetic operators have been overloaded to operate on an instance of Image. + Example: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +% performs threshold by using the 'gt' operator +\end_layout + +\begin_layout Plain Layout + +img = Image.read('coins.png'); +\end_layout + +\begin_layout Plain Layout + +bin = img > 80; +\end_layout + +\begin_layout Plain Layout + +show(img); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Unary operators +\end_layout + +\begin_layout Standard +Unary operators require a single image instance, + and sometimes a scalar argument. +\end_layout + +\begin_layout Minisec +abs +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +abs +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Absolute value of image elements. +\end_layout + +\begin_layout Minisec +exp +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +exp +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes exponential of image elements. +\end_layout + +\begin_layout Minisec +sqrt +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +sqrt +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes square root of image elements. +\end_layout + +\begin_layout Minisec +mpower +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +mpower +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes power of image elements. +\end_layout + +\begin_layout Minisec +log +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +log +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes natural logarithm of image elements. +\end_layout + +\begin_layout Minisec +log10 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +log10 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes decimal logarithm of image elements. +\end_layout + +\begin_layout Minisec +log2 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +log2 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes binary logarithm of image elements. +\end_layout + +\begin_layout Minisec +sign +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +sign +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the sign of image elements as a new image. +\end_layout + +\begin_layout Subsection +Binary operators +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Binary-Operators" + +\end_inset + + +\end_layout + +\begin_layout Standard +Binary operators require two image instances as input, + and return a the result as a new Image. +\end_layout + +\begin_layout Minisec +max +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +max +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the element-wise maximum between two images. +\end_layout + +\begin_layout Minisec +min +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +min +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the element-wise minimum between two images. +\end_layout + +\begin_layout Minisec +minus +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +minus +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Subtracts two images. +\end_layout + +\begin_layout Minisec +plus +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +plus +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Adds two images. +\end_layout + +\begin_layout Minisec +mtimes +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +mtimes +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Multiplies two images element-wise. +\end_layout + +\begin_layout Minisec +times +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +times +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Allows the pixel-wise or voxel-wise multiplication of two images. +\end_layout + +\begin_layout Minisec +mrdivide +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +mrdivide +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Divides two images element-wise. +\end_layout + +\begin_layout Minisec +uplus +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +uplus +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Overloads unary plus for images. +\end_layout + +\begin_layout Minisec +uminus +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +uminus +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Overloads unary minus for images. +\end_layout + +\begin_layout Minisec +absdiff +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +absdiff +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes absolute difference between two images. +\end_layout + +\begin_layout Subsection +Comparison operators +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Comparison-Operators" + +\end_inset + +Comparison operators require two image instances and return a binary image as result. +\end_layout + +\begin_layout Minisec +eq +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +eq +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes equality and returns a binary image. +\end_layout + +\begin_layout Minisec +ge +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ge +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes greater than or equal and returns a binary image. +\end_layout + +\begin_layout Minisec +gt +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +gt +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes greater than and returns a binary image. +\end_layout + +\begin_layout Minisec +le +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +le +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes less than or equal and returns a binary image. +\end_layout + +\begin_layout Minisec +lt +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +lt +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes less than and returns a binary image. +\end_layout + +\begin_layout Minisec +ismember +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ismember +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns a binary image corresponding to the result of the specified logical test. + +\end_layout + +\begin_layout Subsection +Boolean operators +\end_layout + +\begin_layout Standard +Comparison operators require two binary image instances as input, + and return a binary image as result. +\begin_inset Note Note +status open + +\begin_layout Plain Layout +add some examples +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +or +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +or +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes boolean +\begin_inset Quotes sld +\end_inset + +or +\begin_inset Quotes srd +\end_inset + + between two binary images. +\end_layout + +\begin_layout Minisec +and +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +and +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes boolean +\begin_inset Quotes sld +\end_inset + +and +\begin_inset Quotes srd +\end_inset + + between two binary images. +\end_layout + +\begin_layout Minisec +xor +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +xor +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes boolean +\begin_inset Quotes sld +\end_inset + +xor +\begin_inset Quotes srd +\end_inset + + between two binary images. +\end_layout + +\begin_layout Minisec +not +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +not +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Complements a binary image. +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Multi-channel and color images +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Multi-Channel-Images" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +Color images +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Multi-channel +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +channel +\end_layout + +\end_inset + + images store data in a multi-dimensional array with fourth dimension greater that 1. + Multi-channel images include color images. + Conversion from a 3D scalar array can be performed through: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +Iv = Image('Data', + I.Data, + 'vector', + true); +\end_layout + +\end_inset + +Several functions allows intuitive manipulation of channels, + whatever the image dimensionality. +\end_layout + +\begin_layout Minisec +channelCount +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +channelCount +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the number of channels of the image (equal to 1 for single-channel images). +\end_layout + +\begin_layout Minisec +channel +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +channel +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the specified channel from a multi-channel image. +\end_layout + +\begin_layout Minisec +catChannels +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +catChannels +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Concatenates several images to create a multi-channel image. +\end_layout + +\begin_layout Minisec +splitChannels +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +splitChannels +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Splits a multi-channel image into several intensity images. +\end_layout + +\begin_layout Minisec +norm +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +norm +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the norm of a multi-channel image and returns the result as an intensity image. +\end_layout + +\begin_layout Section +Frame manipulation +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Time-Lapse-Images" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +time-lapse image +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Time-lapse images store data in a multi-dimensional array with fifth dimension greater that 1. + Manipulation of time-lapse images is still under development. +\end_layout + +\begin_layout Minisec +frameCount +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +frameCount +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the number of frames of the image (equal to 1 for non movie images). +\end_layout + +\begin_layout Minisec +frame +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +frame +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +frame +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the specified frame from a time-lapse image. +\end_layout + +\begin_layout Minisec +catFrames +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +catFrames +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Concatenates several images to create a time-lapse image. +\end_layout + +\begin_layout Minisec +splitFrames +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +splitFrames +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Splits a time-lapse image into several single frame images. +\end_layout + +\begin_layout Section +Type conversion +\end_layout + +\begin_layout Standard +Several methods are overloaded to facilitate conversion between image data types. +\end_layout + +\begin_layout Subsection +Scalar type conversions +\end_layout + +\begin_layout Standard +Most type conversion functions have been overloaded for the Image type. +\end_layout + +\begin_layout Minisec +convertType +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +convertType +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts the type of image data array to the specified type. +\end_layout + +\begin_layout Minisec +double +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +double +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an intensity image containing double values. +\end_layout + +\begin_layout Minisec +int8 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +int8 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an image containing int8 values. +\end_layout + +\begin_layout Minisec +int16 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +int16 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an image containing int16 values. +\end_layout + +\begin_layout Minisec +int32 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +int32 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an image containing int32 values. +\end_layout + +\begin_layout Minisec +uint8 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +uint8 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an image containing uint8 values. +\end_layout + +\begin_layout Minisec +uint16 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +uint16 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an image containing uint16 values. +\end_layout + +\begin_layout Minisec +uint32 +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +uint32 +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts image to an image containing uint32 values. +\end_layout + +\begin_layout Subsection +Color conversions +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Color-Conversions" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +color +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The functions listed here concern conversion from and between color spaces +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +color space +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Minisec +rgb2gray +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +rgb2gray +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts a RGB color image into a grayscale image. +\end_layout + +\begin_layout Minisec +rgb2hsv +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +rgb2hsv +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts a RGB color image into its corresponding HSV (Hue-Saturation-Value) representation. +\end_layout + +\begin_layout Minisec +hsv2rgb +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +hsv2rgb +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts a HSV (Hue-Saturation-Value) color image into its corresponding RGB representation. +\end_layout + +\begin_layout Minisec +double2rgb +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +double2rgb +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Create a RGB image from values of an intensity image. +\end_layout + +\begin_layout Subsection +Complex images +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Complex-Images" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +Complex images +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Complex images are represented as multi-channel (vector) image with two channels, + one for the each of the real and imaginary parts. + Several functions allows specific processing of complex images. +\end_layout + +\begin_layout Minisec +imag +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +imag +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the imaginary part of a complex image and returns a new intensity image. +\end_layout + +\begin_layout Minisec +real +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +real +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the real part of a complex image and returns a new intensity image. +\end_layout + +\begin_layout Minisec +angle +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +angle +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Extracts the angle of each element of a complex image and returns a new intensity image. +\end_layout + +\begin_layout Minisec +fft +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +fft +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the Fourier representation of an image and returns a complex image. +\end_layout + +\begin_layout Minisec +fftshift +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +fftshift +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Shifts the values of an image to represent the low-frequencies in the middle of the image. +\end_layout + +\begin_layout Minisec +ifft +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ifft +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Reconstructs an image from its Fourier representation (returns a complex image). +\end_layout + +\begin_layout Minisec +ifftshift +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ifftshift +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Shifts the values of an image to represent the low-frequencies in the corners of the image. +\end_layout + +\begin_layout Chapter +Image processing +\end_layout + +\begin_layout Standard +A large number of image processing functions has been overloaded for the Image class. + As for array operations, + the spatial calibration of the input image(s) is kept when possible, + and the image type is also inferred automatically. +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Contrast manipulation +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:adjustDynamic" + +\end_inset + +adjustDynamic +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +adjustDynamic +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +dynamic +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Adjusts the dynamic of the input intensity or grayscale image to better use the [0 255] range (see Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Adjust-Dynamic" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:invert" + +\end_inset + +invert +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +invert +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the complement of the original image. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/dynamic/pout.png + width 25text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Original image +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/dynamic/pout_adjusted.png + width 25text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Adjusted Dynamic +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/dynamic/pout_histograms.png + lyxscale 50 + width 40text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Comparison of histograms +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Adjust-Dynamic" + +\end_inset + +Result of dynamic adjustment. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Figure +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Adjust-Dynamic" +nolink "false" + +\end_inset + + presents the result of dynamic adjustment, + using the following lines of code: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('pout.tif'); +\end_layout + +\begin_layout Plain Layout + +img2 = adjustDynamic(img, + .01); +\end_layout + +\begin_layout Plain Layout + +figure; + +\end_layout + +\begin_layout Plain Layout + +subplot(211); + histogram(img); +\end_layout + +\begin_layout Plain Layout + +subplot(212); + histogram(img2); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Regional filtering +\end_layout + +\begin_layout Standard +The operators depend on a neighborhood of finite size around each pixel or voxel. + They comprise linear operators (the output value is a linear combination of the values in the neighborhood), + or order filters +\begin_inset Index idx +range none +pageformat default +status open + +\begin_layout Plain Layout +order filter +\end_layout + +\end_inset + + such as median. +\end_layout + +\begin_layout Subsection +Generic filter +\end_layout + +\begin_layout Minisec +filter +\begin_inset CommandInset label +LatexCommand label +name "op:filter" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +filter +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +filter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Wrapper for the +\begin_inset Quotes sld +\end_inset + +imfilter +\begin_inset Quotes srd +\end_inset + + function from Matlab. +\end_layout + +\begin_layout Subsection +Smoothing / low-pass filters +\end_layout + +\begin_layout Standard +Low-pass filters remove details with small size, + and tend to blur the resulting image. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('rice.png'); +\end_layout + +\begin_layout Plain Layout + +imgBox = boxFilter(img, + [5 5]); +\end_layout + +\begin_layout Plain Layout + +imgGauss = gaussianFilter(img, + [5 5], + 2); +\end_layout + +\begin_layout Plain Layout + +imgMed = medianFilter(img, + ones([5 5])); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/linearFilters/rice-boxFilter5x5.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Box filter 5x5 +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/linearFilters/rice-gaussFilter5x5s2.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Gaussian filter 5x5, + sigma = 2 +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/linearFilters/rice-medFilter5x5.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Median filter 5x5 +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Smooth-Image" + +\end_inset + +Comparison of several noise removal filters. + Median filters tends to better preserve edges than box or gaussian filters. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +boxFilter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +boxFilter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the average value in a rectangular neighborhood around each pixel/voxel. +\end_layout + +\begin_layout Minisec +gaussianFilter +\begin_inset CommandInset label +LatexCommand label +name "op:gaussianFilter" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +gaussianFilter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes Gaussian filter on the image, + by applying Gaussian kernels along each dimension. +\end_layout + +\begin_layout Minisec +medianFilter +\begin_inset CommandInset label +LatexCommand label +name "op:medianFilter" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +medianFilter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the median value within a given neighborhood of each pixel/voxel. +\end_layout + +\begin_layout Minisec +meanFilter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +meanFilter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the average of values in a given neighborhood of each pixel/voxel. +\end_layout + +\begin_layout Subsection +Gradients +\end_layout + +\begin_layout Standard +Gradient operators aim at enhancing structures within the image such as edges or spots. +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:gradient" + +\end_inset + +gradient +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the gradient of the image along each dimension, + and returns a +\series bold +vector image +\series default + with as many channels as the number of image dimensions (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Gradients-Of-Intensity-Image" +nolink "false" + +\end_inset + +). + See also the morphological gradient (section +\begin_inset CommandInset ref +LatexCommand ref +reference "op:morphoGradient" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + Exemple of use of the gradient function: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +I = Image.read('rice.png'); +\end_layout + +\begin_layout Plain Layout + +grad = gradient(I); + +\end_layout + +\begin_layout Plain Layout + +% the result is given as a vector image +\end_layout + +\begin_layout Plain Layout + +figure; + show(norm(grad),[]); +\end_layout + +\begin_layout Plain Layout + +Gx = channel(grad, + 1); +\end_layout + +\begin_layout Plain Layout + +Gy = channel(grad, + 2); +\end_layout + +\begin_layout Plain Layout + +% alternative: + [Gx,Gy] = gradient(I); +\end_layout + +\begin_layout Plain Layout + +figure; + subplot(121);show(Gx, + []); + subplot(122);show(Gy, + []); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/linearFilters/rice_gradientNorm.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +norm(grad) +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/linearFilters/rice_gradientX.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +channel(grad,1) +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/linearFilters/rice_gradientY.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +channel(grad,2) +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Gradients-Of-Intensity-Image" + +\end_inset + +Gradients computed on an intensity image. + (a) Norm of the gradient. + (b) Gradient along the x direction. + (c) Gradient along the y direction. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Morphological filtering +\end_layout + +\begin_layout Subsection +Local morphological filters +\end_layout + +\begin_layout Standard +Many morphological filters depend on a neighborhood specified by a +\series bold + structuring element +\series default + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +structuring element +\end_layout + +\end_inset + + +\begin_inset CommandInset citation +LatexCommand citep +key "Soille_2003" +literal "false" + +\end_inset + +. + The structuring element is a binary neighborhood defined by its shape and its size. + Typical shapes for 2D structuring elements are the square (faster to process), + the disk (that better preserves the shape of the structures), + line segments, + diamonds and octagons. +\end_layout + +\begin_layout Subsubsection +Erosion and dilation +\end_layout + +\begin_layout Standard +The most basic operators are the dilation and the erosion. + For a grayscale image, + the +\series bold +morphological dilation +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +dilation +\end_layout + +\end_inset + + +\series default + computes for each pixel the maximum within its neighborhood (defined by the structuring element), + whereas the +\series bold +morphological erosion +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +erosion +\end_layout + +\end_inset + + +\series default + considers the minimum value within the neighborhood (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Morphological-Dilation-Erosion" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/morpho/rice.png + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Original image. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/morpho/rice-Dilation-Sq2.png + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Dilation. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/morpho/rice-Erosion-Sq2.png + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Erosion. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Morphological-Dilation-Erosion" + +\end_inset + +Some examples of morphological filters on a grayscale image. + (a) Original image. + (a) Result of a dilation with a +\begin_inset Formula $5\times5$ +\end_inset + + square structuring element. + (c) Result of an erosion with the same structuring element. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Applying a dilation or an erosion changes the size of the structures in the image: + the grains in the result of the dilation image are larger. + These operations may also merge, + separate, + or make some components of the image disappear. + +\end_layout + +\begin_layout Minisec +dilation +\begin_inset CommandInset label +LatexCommand label +name "op:dilation" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +dilation +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +dilation +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a morphological dilation on the input image. +\end_layout + +\begin_layout Minisec +erosion +\begin_inset CommandInset label +LatexCommand label +name "op:erosion" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +erosion +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +erosion +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a morphological erosion on the input image. +\end_layout + +\begin_layout Subsubsection +Closing and Opening +\end_layout + +\begin_layout Standard +Morphological dilation and erosion are often used in combination for removing noise within images. + For example, + the result of a dilation followed by an erosion is called a +\series bold +morphological closing +\series default +, + and removes dark structures smaller than the structuring element (Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Morphological-Closing-Opening" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-b). + It can also connect bright structures that were separated by a thin dark space. + In a symmetric way, + the result of an erosion followed by a dilation is called a +\series bold +morphological opening +\series default +, + and removes bright structures smaller than the structuring element (Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Morphological-Closing-Opening" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-c). +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/morpho/rice.png + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Original image. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/morpho/rice-Closing-Sq2.png + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Closing. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/morpho/rice-Opening-Sq2.png + width 32text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Opening. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Morphological-Closing-Opening" + +\end_inset + +Other examples of morphological filters on a grayscale image. + (a) Original image. + (a) Result of a morphological closing with a +\begin_inset Formula $5\times5$ +\end_inset + + square structuring element. + (c) Result of a morphological opening with the same structuring element. + The grains tend to separate from each others. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +closing +\begin_inset CommandInset label +LatexCommand label +name "op:closing" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +closing +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +morphological closing +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a morphological closing (dilation followed by erosion) on the input image, + +\series bold +removing dark structures +\series default + smaller than the structuring element. +\end_layout + +\begin_layout Minisec +opening +\begin_inset CommandInset label +LatexCommand label +name "op:opening" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +opening +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +morphological opening +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a morphological opening (erosion followed by dilation) on the input image, + +\series bold +removing bright structures +\series default + smaller than the structuring element. +\end_layout + +\begin_layout Subsubsection +Other morphological filters +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:morphoGradient" + +\end_inset + +morphoGradient +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +morphoGradient +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the morphological gradient (also known as the +\begin_inset Quotes sld +\end_inset + +Beucher gradient +\begin_inset Quotes srd +\end_inset + +) on the input image. + The morphological gradient is the difference of a morphological dilation with a morphological erosion, + computed with the same structuring element. + This results in +\series bold +enhancing edges +\series default + of the image. + See also the (linear) gradient operator (section +\begin_inset CommandInset ref +LatexCommand ref +reference "op:gradient" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +morphoLaplacian +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +morphoLaplacian +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +laplacian +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the morphological laplacian on the input image. + As for the morphological gradient, + this results in +\series bold +enhancing edges +\series default + of the image. + The results is returned as an intensity image. +\end_layout + +\begin_layout Minisec +whiteTopHat +\begin_inset CommandInset label +LatexCommand label +name "op:whiteTopHat" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +whiteTopHat +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +top-hat +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the White Top-Hat operator, + that +\series bold +enhances bright structures +\series default + smaller than the structuring element. +\end_layout + +\begin_layout Minisec +blackTopHat +\begin_inset CommandInset label +LatexCommand label +name "op:blackTopHat" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +blackTopHat +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +top-hat +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the Black Top-Hat operator, + that +\series bold +enhances dark structures +\series default + smaller than the structuring element. +\end_layout + +\begin_layout Minisec +directionalFilter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +directionalFilter +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +directional filter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Applies a directional morphological filter on original image (useful for enhancing curvilinear structures). +\end_layout + +\begin_layout Subsection +Connected component filters +\end_layout + +\begin_layout Standard +Another family of morphological filters relies on the connected components that can be obtained within a grayscale or binary image. + Such operators depend on a connectivity option. + +\end_layout + +\begin_layout Subsubsection +Morphological reconstruction +\end_layout + +\begin_layout Standard +Morphological reconstruction is the core methods of many other tools. + It requires two images: + one image (the +\begin_inset Quotes sld +\end_inset + +marker +\begin_inset Quotes srd +\end_inset + +) is used to initialize the reconstruction, + another image (the +\begin_inset Quotes sld +\end_inset + +mask +\begin_inset Quotes srd +\end_inset + +) is used to constrain the reconstruction +\begin_inset CommandInset citation +LatexCommand citep +key "Soille_2003" +literal "false" + +\end_inset + +. +\end_layout + +\begin_layout Minisec +reconstruction +\begin_inset CommandInset label +LatexCommand label +name "op:reconstruction" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +reconstruction +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +morphological reconstruction +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Morphological reconstruction of binary or grayscale image. +\end_layout + +\begin_layout Subsubsection +Minima and maxima +\end_layout + +\begin_layout Minisec +regionalMaxima +\begin_inset CommandInset label +LatexCommand label +name "op:regionalMaxima" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionalMaxima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regional maxima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +maxima (regional) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes regional maxima. +\end_layout + +\begin_layout Minisec +regionalMinima +\begin_inset CommandInset label +LatexCommand label +name "op:regionalMinima" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionalMinima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regional minima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +minima (regional) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes regional minima. +\end_layout + +\begin_layout Minisec +extendedMaxima +\begin_inset CommandInset label +LatexCommand label +name "op:extendedMaxima" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +extendedMaxima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +extendedMaxima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +maxima (extended) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes extended maxima based on dynamic value and optionnaly the connectivity. +\end_layout + +\begin_layout Minisec +extendedMinima +\begin_inset CommandInset label +LatexCommand label +name "op:extendedMinima" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +extendedMinima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +extended minima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +minima (extended) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes extended minima based on dynamic value and optionnaly the connectivity. +\end_layout + +\begin_layout Minisec +imposeMaxima +\begin_inset CommandInset label +LatexCommand label +name "op:imposeMaxima" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +imposeMaxima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +maxima imposition +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Imposes binary maxima image on an intensity or grayscale image. +\end_layout + +\begin_layout Minisec +imposeMinima +\begin_inset CommandInset label +LatexCommand label +name "op:imposeMinima" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +imposeMinima +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +minima imposition +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Imposes binary minima image on an intensity or grayscale image. +\end_layout + +\begin_layout Subsubsection +Applications +\end_layout + +\begin_layout Minisec +areaOpening +\begin_inset CommandInset label +LatexCommand label +name "op:areaOpening" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +areaOpening +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +area opening +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Remove connected components of a binary image based on a size criterion. +\end_layout + +\begin_layout Minisec +attributeOpening +\begin_inset CommandInset label +LatexCommand label +name "op:attributeOpening" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +attributeOpening +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +attribute opening +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Remove connected components of a binary image based on a size or elongation criterion. +\end_layout + +\begin_layout Minisec +fillHoles +\begin_inset CommandInset label +LatexCommand label +name "op:fillHoles" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +fillHoles +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +fill holes +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Removes holes within grayscale or intensity image, + using morphological reconstruction. +\end_layout + +\begin_layout Minisec +killBorders +\begin_inset CommandInset label +LatexCommand label +name "op:killBorders" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +killBorders +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +kill borders +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Kills the borders of a grayscale or intensity image, + using morphological reconstruction. +\end_layout + +\begin_layout Minisec +floodFill +\begin_inset CommandInset label +LatexCommand label +name "op:floodFill" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +floodFill +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +flood fill +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a flood-fill from a given position, + by propagating along similar values and replacing with the specified value. +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Image segmentation +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Segmentation-Methods" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +segmentation +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The aim of image segmentation is to identify relevant structures within grayscale or color images. + The result can be a binary image if only one structure has to be identified. + For representing the segmentation of several regions, + a common approach is to use a label image (or label map), + in which each pixel or voxel contains the label of the region it belongs to. + By convention, + the value 0 is often assigned to the background. + Several utilities for label images are presented in section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Label-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Standard +In some cases, + segmentation algorithms may return results as a geometric data structure, + such as a polygon or a polygonal mesh. +\end_layout + +\begin_layout Subsection +Binarization +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "subsec:Binarization" + +\end_inset + + +\end_layout + +\begin_layout Standard +Binarization operators compute a binary image from a grayscale or intensity image. + Manual threshold can be obtained via relational operators (section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Comparison-Operators" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +): +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('coins.png'); +\end_layout + +\begin_layout Plain Layout + +>> segMan = img > 80; +\end_layout + +\begin_layout Plain Layout + +>> figure; + show(overlay(img, + segMan, + 'r');); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Several methods are provided that implement automated determination of the threshold value. +\end_layout + +\begin_layout Minisec +binarize +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +binarize +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +binarization +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a binarisation of the input grayscale or intensity image. + The threshold value can be manually specified, + or computed automatically (using Otsu's method). +\end_layout + +\begin_layout Minisec +otsuThreshold +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +otsuThreshold +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +Otsu threshold +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a binarisation of the input grayscale image using Otsu method for computing the threshold (Figure +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Segmentation-Coin-Image" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + See also the multi-level Otsu method (page +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:multiLevelOtsuThreshold" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +otsuThresholdValue +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +otsuThresholdValue +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the value used for Otsu threshold. +\end_layout + +\begin_layout Minisec +maxEntropyThreshold +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +maxEntropyThreshold +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +max entropy threshold +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a binarisation of the input grayscale image using maximisation of entropies method for computing the threshold. +\end_layout + +\begin_layout Minisec +maxEntropyThresholdValue +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +maxEntropyThresholdValue +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the value used for max-entropy threshold. +\end_layout + +\begin_layout Standard +\begin_inset space ~ +\end_inset + + +\begin_inset Newline newline +\end_inset + +The +\series bold +isosurface operator +\series default + (page +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:isosurface" +nolink "false" + +\end_inset + +) can also return the mesh resulting from 3D segmentation of an intensity / grayscale image. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/segment/coins-segMan-ovr.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Manual +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/segment/coins-segOtsu-ovr.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Otsu +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/segment/coins-segWat-rgb.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Watershed +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Segmentation-Coin-Image" + +\end_inset + +Segmentation of a grayscale image using various methods. + (a) Manual threshold. + (b) Otsu threshold. + (c) Watershed algorithm computed on image gradient. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Subsection +Multi-level thresholding +\end_layout + +\begin_layout Standard +Multi-level thresholding peforms a clustering of pixels values according to their gray levels. +\end_layout + +\begin_layout Minisec +multiLevelOtsuThreshold +\begin_inset CommandInset label +LatexCommand label +name "op:multiLevelOtsuThreshold" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +multiLevelOtsuThreshold +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +multi-level Otsu method +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Performs a segmentation of the input grayscale image into several classes using multi-level Otsu method for computing the thresholds. +\end_layout + +\begin_layout Subsection +Clustering +\end_layout + +\begin_layout Standard +K-means is a method for cluster analysis. + It consists in building a partition of +\begin_inset Formula $n$ +\end_inset + + observations into +\begin_inset Formula $k$ +\end_inset + + clusters in which each observation belongs to the cluster with the nearest mean, + serving as a prototype of the cluster. + In the case of a grayscale image, + this results in a muti-level threshold based on images intensities. + In the case of a multi-variate image, + each observation corresponds to an image element, + and each component corresponds a feature. + The resulting clusters correspond to groups of pixels with similar or near colors or channel intensities. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/process/peppers_kmeans6_classes.png + width 48text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Labels +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/process/peppers_kmeans6_averageColor.png + width 48text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Average colors +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:kmeans-colorImage-classes" + +\end_inset + +Result of the kmeans method applied to a color image. + (a) The colors correspond to the class index. + (b) The colors correspond to the average color within the corresponding class. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The following script provides an example of k-means clustering of a RGB image. + The result is given on Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:kmeans-colorImage-classes" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. + The combination of classes with the values of class centroids allows to build a synthetic image with as many colors as the number of classes, + that resembles the original image (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:kmeans-colorImage-classes" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-b). +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('peppers.png'); +\end_layout + +\begin_layout Plain Layout + +[idx, + centroids] = kmeans(img, + 6); + % 'idx' is the image of class indices +\end_layout + +\begin_layout Plain Layout + +% display the classes with arbitrary colors +\end_layout + +\begin_layout Plain Layout + +rgb = label2rgb(idx, + 'jet', + 'w'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(rgb) +\end_layout + +\begin_layout Plain Layout + +% display the classes with average color of each class +\end_layout + +\begin_layout Plain Layout + +map = centroids / 255; + % average color of each class +\end_layout + +\begin_layout Plain Layout + +rgb = label2rgb(idx, + map); + % convert to RGB +\end_layout + +\begin_layout Plain Layout + +figure; + show(rgb) +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Minisec +kmeans +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +kmeans +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +k-means segmentation +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Applies k-means clustering on an image, + and returns a label image containing the class of each pixel. + Can also return an array with the coordinates of the centroid of each class. +\end_layout + +\begin_layout Subsection +Watershed +\end_layout + +\begin_layout Standard +The waterhed is an operator from mathematical morphology. + It results in a label image (see Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Label-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +watershed +\begin_inset CommandInset label +LatexCommand label +name "op:watershed" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +watershed +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +watershed +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Identifies catchment basins within a grayscale or intensity image using a watershed algorithm (Figure +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Segmentation-Coin-Image" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + The catchment +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +catchment basin +\end_layout + +\end_inset + + basins can be further converted to a color image via the label2rgb methods (Page +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:label2rgb" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +>> img = Image.read('coins.png'); +\end_layout + +\begin_layout Plain Layout + +>> grad = norm(gradient(img)); +\end_layout + +\begin_layout Plain Layout + +>> gradf = boxFilter(grad, + [5 5]); +\end_layout + +\begin_layout Plain Layout + +>> emin = extendedMinima(gradf, + 20, + 4); +\end_layout + +\begin_layout Plain Layout + +>> grad2 = imposeMinima(gradf, + emin, + 4); +\end_layout + +\begin_layout Plain Layout + +>> wat = watershed(grad2, + 4); +\end_layout + +\begin_layout Plain Layout + +>> rgb = label2rgb(wat, + 'jet', + 'w'); +\end_layout + +\begin_layout Plain Layout + +>> figure; + show(rgb); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Chapter +Binary and label images +\end_layout + +\begin_layout Standard +Some operators are specific to images with categorical data types, + such as binary images or label images. +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Binary images +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Binary-images" + +\end_inset + + +\end_layout + +\begin_layout Standard +Binary images contains only 0 and 1 values. + They can represent the result of a segmentation. + Figure +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Binary-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + represents the result of the following script: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('circles.png'); +\end_layout + +\begin_layout Plain Layout + +% compute distance map, + and display with color map +\end_layout + +\begin_layout Plain Layout + +distmap = distanceMap(img); +\end_layout + +\begin_layout Plain Layout + +figure; + show(distmap); + colormap parula; +\end_layout + +\begin_layout Plain Layout + +% compute skeleton, + and overlay on original image +\end_layout + +\begin_layout Plain Layout + +figure; + show(overlay(img, + skeleton(img))); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/binary/circles-distmap-rgb.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Distance map. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/binary/circles-boundary-ovr.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Boundary. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status collapsed + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/binary/circles-skeleton-ovr.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Skeleton. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\align center +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Binary-Images" + +\end_inset + +Utilities for binary images: + distance map (represented using a color code), + boundary, + and skeleton (overlaid on original image). +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Distance maps +\end_layout + +\begin_layout Minisec +distanceMap +\begin_inset CommandInset label +LatexCommand label +name "op:distanceMap" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +distanceMap +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +distance map +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes distance map of each pixel/voxel within a binary image (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Binary-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-a). +\end_layout + +\begin_layout Minisec +chamferDistanceMap +\begin_inset CommandInset label +LatexCommand label +name "op:chamferDistanceMap" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +chamferDistanceMap +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +chamfer distance map +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Another implementation of distance maps, + that allows to compute distance map for label images, + even in the case of adjacent regions +\begin_inset CommandInset citation +LatexCommand citep +key "Borgefors_1984_CVGIP,Borgefors_1986_CVGIP" +literal "false" + +\end_inset + +. +\end_layout + +\begin_layout Minisec +geodesicDistanceMap +\begin_inset CommandInset label +LatexCommand label +name "op:geodesicDistanceMap" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +geodesicDistanceMap +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +geodesic distance map +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Progagates the distance from a binary marker image, + constrained to another binary image (the mask) +\begin_inset CommandInset citation +LatexCommand citep +key "Soille_2003" +literal "false" + +\end_inset + +. + An example is shown in Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Geodesic-Distance-Map" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/binary/circles-geodDistMap.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\align center +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Geodesic-Distance-Map" + +\end_inset + +Computation of geodesic distance map within a binary mask. + The marker is shown as a red cross. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Morphological operators +\end_layout + +\begin_layout Minisec +boundary +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +boundary +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +boundary +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes binary image containing boundary pixels/voxels of a binary image (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Binary-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-b). +\end_layout + +\begin_layout Minisec +skeleton +\begin_inset CommandInset label +LatexCommand label +name "op:skeleton" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +skeleton +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +skeleton +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the skeleton of a binary image (Fig. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Binary-Images" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +-c). +\end_layout + +\begin_layout Subsection +Utility methods +\end_layout + +\begin_layout Minisec +find +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +find +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes coordinates of non-zeros pixels or voxels within a binary image. +\end_layout + +\begin_layout Minisec +largestRegion +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +largestRegion +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the binary image corresponding to the largest region within binary image. +\end_layout + +\begin_layout Minisec +overlay +\begin_inset CommandInset label +LatexCommand label +name "op:overlay" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +overlay +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +overlay +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Applies a binary overlay on a color or grayscale image, + resulting is a color image. +\end_layout + +\begin_layout Section +Label Images +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Label-Images" + +\end_inset + + +\end_layout + +\begin_layout Standard +Label images +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +label image +\end_layout + +\end_inset + + are often used to represent results of segmentation +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +segmentation +\end_layout + +\end_inset + + algorithms (section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Segmentation-Methods" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). + Each pixel contains either the index of the region it belongs to, + or the value 0, + corresponding to the background. +\end_layout + +\begin_layout Standard +The following listing provides an example of use of some of the functions, + with results displayed in Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Connected-Component-Labeling" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +. +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +img = Image.read('coins.png'); +\end_layout + +\begin_layout Plain Layout + +bin = opening(img > 80, + ones(3, + 3)); +\end_layout + +\begin_layout Plain Layout + +lbl = componentLabeling(bin); +\end_layout + +\begin_layout Plain Layout + +figure; + show(lbl); +\end_layout + +\begin_layout Plain Layout + +rgb = label2rgb(lbl, + 'jet', + 'w'); +\end_layout + +\begin_layout Plain Layout + +figure; + show(rgb); +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/label/coins-bin.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Binary image. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/label/coins-componentLabeling-gray8.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Connected Components. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\begin_inset Float figure +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename images/label/coins-componentLabeling-rgb.png + width 30text% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Colorization of labels. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:Connected-Component-Labeling" + +\end_inset + +Various steps of connected components algorithm. + (a) The original binary image. + (b) The connected companents displayed in grayscale. + (c) The connected components after conversion to an RGB image. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Processing methods +\end_layout + +\begin_layout Minisec +componentLabeling +\begin_inset CommandInset label +LatexCommand label +name "op:componentLabeling" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +componentLabeling +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +connected component labeling +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the label map corresponding to the connected components in a binary image. +\end_layout + +\begin_layout Minisec +label2rgb +\begin_inset CommandInset label +LatexCommand label +name "op:label2rgb" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +label2rgb +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Converts a label image to a RGB color image using the specified LUT. +\end_layout + +\begin_layout Minisec +findRegionLabels +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +findRegionLabels +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the set of unique labels within a label image. +\end_layout + +\begin_layout Minisec +cropLabel +\begin_inset CommandInset label +LatexCommand label +name "op:cropLabel" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +cropLabel +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +crop +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Crops the region containing a given label. + See also the cropOrientedBox function (p. +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:cropOrientedBox" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + +). +\end_layout + +\begin_layout Minisec +\begin_inset CommandInset label +LatexCommand label +name "op:regionIsosurface" + +\end_inset + +regionIsosurface +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionIsosurface +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +isosurface +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Wrapper for the native +\begin_inset Quotes sld +\end_inset + +isosurface +\begin_inset Quotes srd +\end_inset + + function, + that iterates over the different labels within the image to display or compute the isosurface of each region. +\end_layout + +\begin_layout Subsection +Utility methods +\end_layout + +\begin_layout Minisec +regionMinMaxIndices +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionMinMaxIndices +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the indices of minimum and maximum coordinates of the regions along each direction within the binary or label image. +\end_layout + +\begin_layout Chapter +Image analysis +\end_layout + +\begin_layout Standard +Several methods allows to quantify the information within the image. + Currently, + the consider only the analysis of region morphology. + When possible, + the spatial calibration is taken into account into the result. +\end_layout + +\begin_layout Standard +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash +minitoc +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage newpage +\end_inset + + +\end_layout + +\begin_layout Section +Region analysis +\end_layout + +\begin_layout Standard +\begin_inset CommandInset label +LatexCommand label +name "sec:Region-Analysis" + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +region +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Image analysis operations are also available in the library. + Most methods require a binary or label image as input. + Most methods follow the name pattern +\begin_inset Quotes sld +\end_inset + +regionXXXXs +\begin_inset Quotes srd +\end_inset + +, + where +\begin_inset Quotes sld +\end_inset + +XXXX +\begin_inset Quotes srd +\end_inset + + corresponds to the feature to analyse. +\end_layout + +\begin_layout Subsection +Global analysis +\end_layout + +\begin_layout Minisec +analyzeRegions +\begin_inset CommandInset label +LatexCommand label +name "op:analyzeRegions" + +\end_inset + + +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +analyzeRegions +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes a series of features for the regions within a label map. + In practive, + this method is simply a wrapper for the +\begin_inset Quotes sld +\end_inset + +regionprops +\begin_inset Quotes srd +\end_inset + + function from the Image Processing Toolbox. + Returns a struct with field names corresponding to measured properties. +\end_layout + +\begin_layout Subsection +Basic features +\end_layout + +\begin_layout Standard +The methods in this section allows to retrieve the position and/or the extent of the regions. +\end_layout + +\begin_layout Minisec +regionElementCount +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionElementCount +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Returns the number of elements (pixels or voxels) of each region within the binary or label image. + Do not take into account spatial calibration, + contrary to methods regionArea and regionVolume. +\end_layout + +\begin_layout Minisec +regionBoundingBox +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionBoundingBox +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +bounding box +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the bounding box of each region within the binary/label 2D or 3D image, + and returns a N-by-4 or N-by-6 array of coordinate extents. +\end_layout + +\begin_layout Minisec +regionCentroid +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionCentroid +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +centroid +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the centroid of each region within the binary/label 2D or 3D image, + and returns a N-by-2 or N-by-3 array of coordinates. +\end_layout + +\begin_layout Subsection +General size features +\end_layout + +\begin_layout Standard +The +\series bold +intrinsic volumes +\series default +are a set of features with interesting mathematical properties that are commonly used for describing individual particles as well as binary microstructures. + In 2D, + they correspond to the +\series bold +area +\series default +, + the +\series bold +perimeter +\series default + and the +\series bold +Euler number +\series default +. + For 3D images, + intrinsic volumes correspond to the +\series bold +volume +\series default +, + the +\series bold + surface area +\series default +, + the +\series bold +mean breadth +\series default + and the +\series bold +3D Euler number +\series default +. + +\end_layout + +\begin_layout Minisec +regionArea +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionArea +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the area +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +area +\end_layout + +\end_inset + + of regions within a 2D binary or label image. + Consists in counting the number of pixels belonging to the regions, + and multiplying by the area of a single pixel. +\end_layout + +\begin_layout Minisec +regionPerimeter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionPerimeter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the perimeter +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +perimeter +\end_layout + +\end_inset + + of regions within a 2D binary or label image. + Uses a discretization of the Crofton formula, + by counting intersections with lines in either 2 or 4 directions. +\end_layout + +\begin_layout Minisec +regionVolume +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionVolume +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the volume +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +volume +\end_layout + +\end_inset + + of regions within a 3D binary or label image. + Consists in counting the number of voxels belonging to the regions, + and multiplying by the volume of a single voxel. +\end_layout + +\begin_layout Minisec +regionSurfaceArea +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionSurfaceArea +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the surface area +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +surface area +\end_layout + +\end_inset + + of regions within a 3D binary or label image. + Uses a discretization of the Crofton formula, + by counting intersections with 3D lines in either 3 or 13 directions +\begin_inset CommandInset citation +LatexCommand citep +key "Schladitz_2006_DGCI,Ohser_2009" +literal "false" + +\end_inset + +. +\end_layout + +\begin_layout Minisec +regionMeanBreadth +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionMeanBreadth +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the mean breadth of regions within a 3D binary or label image. + Uses a discretization of the Crofton formula, + by integrating Euler number computed on the intersections with 3D planes oriented in either 3 or 13 directions. +\end_layout + +\begin_layout Minisec +regionEulerNumber +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionEulerNumber +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the Euler number +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout + +\family roman +\series medium +\shape up +\size normal +\emph off +\nospellcheck off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none +Euler number +\end_layout + +\end_inset + + of regions within a 2D or 3D binary or label image. + Euler number quantifies the topology of a set. + For 2D regions, + it corresponds to the number of connected components, + minus the number of holes. + For 3D regions, + one needs to count in addition the number of bubbles within the regions. +\end_layout + +\begin_layout Subsection +Moments +\end_layout + +\begin_layout Standard +A binary region +\begin_inset Formula $X$ +\end_inset + + may be described mathematically by its +\series bold + geometric moments +\series default + +\begin_inset Formula $m_{pq}$ +\end_inset + + of order +\begin_inset Formula $(p,q)$ +\end_inset + +. + Moments up to the order 2 may be summarized by an equivalent ellipse, + or equivalent ellipsoid in 3D. +\end_layout + +\begin_layout Minisec +regionEquivalentEllipse +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionEquivalentEllipse +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ellipse +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the equivalent ellipse of each region within the binary/label 2D image. + Returns a N-by-5 array containing for each region the coordinates of the centroid, + the length of the two semi-axes, + and the orientation (in degrees). +\end_layout + +\begin_layout Minisec +regionEquivalentEllipsoid +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionEquivalentEllipsoid +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +ellipsoid +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the equivalent ellipsoid of each region within a 3D binary or label image. + Returns a N-by-9 numeric array containing for each region the coordinates of the centroid, + the length of the three semi-axes, + and the orientation (in degrees) as three Euler angles. +\end_layout + +\begin_layout Subsection +Diameters +\end_layout + +\begin_layout Standard +The library also provides several methods for describing the size from +\series bold +diameters +\series default +, + or from +\series bold +equivalent geometries +\series default +. +\end_layout + +\begin_layout Minisec +regionFeretDiameter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionFeretDiameter +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +Feret diameter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the Feret diameter along a given direction of regions within a (2D) binary or label image. +\end_layout + +\begin_layout Minisec +regionMaxFeretDiameter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionMaxFeretDiameter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the Maximum Feret diameter of regions within a (2D) binary or label image. +\end_layout + +\begin_layout Minisec +regionGeodesicDiameter +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionGeodesicDiameter +\end_layout + +\end_inset + + +\begin_inset Index idx +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +geodesic diameter +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the largest geodesic diameter within each region within a (2D) binary or label image +\begin_inset CommandInset citation +LatexCommand citep +key "Lantuejoul_1981_JMicrosc" +literal "false" + +\end_inset + +. +\end_layout + +\begin_layout Minisec +regionInscribedCircle +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionInscribedCircle +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the largest inscribed circle within each region of a 2D binary or label image, + and returns a numeric array containing center coordinates and radius of circle(s). +\end_layout + +\begin_layout Minisec +regionInscribedBall +\begin_inset Index met +range none +pageformat default +status collapsed + +\begin_layout Plain Layout +regionInscribedBall +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Computes the largest inscribed ball within each region of a 3D binary or label image, + and returns a numeric array containing center coordinates and radius of ball(s). +\end_layout + +\begin_layout Chapter +\start_of_appendix +Conversion from Image Processing Toolbox +\end_layout + +\begin_layout Standard +The section provides correspondencies between functions from Matlab's Image Processing Toolbox and Image class toolbox. +\end_layout + +\begin_layout Section +Image creation and import +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-IO" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + provides correspondencies for reading/writing images. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imread +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image.read +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Read" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imwrite +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +write +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Write" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +zeros +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image.zeros +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Image-Creation" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +ones +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image.ones +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Image-Creation" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +false +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image.false +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Image-Creation" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +true +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image.true +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Image-Creation" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-IO" + +\end_inset + +Correspondencies from Image Processing Toolbox for image I/O and creation functions +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Image processing +\end_layout + +\begin_layout Subsection +Geometry +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "tab:Correspondences-Geometry" +nolink "false" + +\end_inset + + provides correspondencies operators working on image geometry. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imcrop +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +crop +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Use a different syntax for specification of box +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:crop" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +flip +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +flip +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +D=1 corresponds to x axis +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:flip" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +rotate90 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +rotate90 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:crop" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +montage +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +montage +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:montage" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +squeeze +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +squeeze +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:crop" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-Geometry" + +\end_inset + +Correspondencies from Image Processing Toolbox for geometry functions +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Contrast +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand pageref +reference "tab:Correspondences-Intensity" +nolink "false" + +\end_inset + + provides correspondencies for operators changing contrast of images. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imadjust +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +adjustDynamic +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Rescale gray levels of image to get better dynamic +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:adjustDynamic" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imcomplement +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +invert +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Invert an image (computes its complement) +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:invert" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-Intensity" + +\end_inset + +Correspondencies from Image Processing Toolbox for intensity operators +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsection +Linear filtering +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-Linear-Filtering" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + provides correspondencies for linear filtering operators. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imfilter +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +filter +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Applies linear filter on the image. + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:filter" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imgaussfilt +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +gaussianFilter +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Gaussian filtering of a grayscale 2D image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:gaussianFilter" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imgaussfilt3 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +gaussianFilter +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Gaussian filtering of a grayscale 3D image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:gaussianFilter" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +medfilt2 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +medianFilter +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Compute median value in the neighboorhood of each pixel +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:medianFilter" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +medfilt3 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +medianFilter +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Compute median value in the neighboorhood of each voxel +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:medianFilter" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imgradient +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Compute gradient image of 2D intensity image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:gradient" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imgradient3 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Compute gradient image of 3D intensity image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:gradient" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imgradientxy +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +gradient +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Compute gradient components of intensity image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:gradient" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-Linear-Filtering" + +\end_inset + +Correspondencies from Image Processing Toolbox for image filtering +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage clearpage +\end_inset + + +\end_layout + +\begin_layout Subsection +Morphological operations +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-IPT-Morphology" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + provides correspondencies between image processing toobox functions and Image-class related to morphological operators. + Tables +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-IPT-Morphology-Binary" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + and +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-IPT-Morphology-Label" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + are more specific to operators on binary and label images respectively. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imbothat +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +blackTopHat +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Black Top-Hat transform of an intensity or binary image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:blackTopHat" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imclearborer +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +killBorder +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Remove borders of a binary or grayscale image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:killBorders" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imclose +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +closing +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Morphological closing +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:closing" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imdilate +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +dilation +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Morphological dilation +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:dilation" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imerode +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +erosion +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Morphological erosion +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:erosion" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imextendedmax +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +extendedMaxima +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Extended maxima of the image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:extendedMaxima" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imextendedmin +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +extendedMinima +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Extended minima of the image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:extendedMinima" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imfill +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +fillHoles +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Fill holes in a binary or grayscale image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:fillHoles" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imhmax +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +NA +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imhmin +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +NA +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imimposmin +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +imposeMinima +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Impose minima on a grayscale or intensity image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:imposeMinima" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imopen +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +opening +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +morphological opening +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:opening" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imreconstruct +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +reconstruction +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Morphological reconstruction of marker image under mask image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:reconstruction" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imregionalmax +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +regionalMaxima +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Regional maxima of a grayscale image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:regionalMaxima" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imregionalmin +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +regionalMinima +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Regional minima of a grayscale image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:regionalMinima" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imtophat +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +whiteTopHat +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +White Top-Hat transform of an intensity or binary image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:whiteTopHat" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +watershed +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +watershed +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Watershed transform of a gray-scale image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:watershed" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +conndef +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +NA +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +iptcheckconn +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +NA +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-IPT-Morphology" + +\end_inset + +Correspondencies from Image Processing Toolbox for morphological operations on +\series bold +grayscale +\series default + images +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwdist +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +distanceMap +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Distance map of a binary image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:distanceMap" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwhitmiss +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +NA +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwmorph +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +skeleton +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Skeleton of a binary image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:skeleton" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +(other operations are not linked) +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwlabel +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +componentLabeling +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Connected component labeling of 2D image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:componentLabeling" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwlabeln +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +componentLabeling +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Connected component labeling of 3D image +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:componentLabeling" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwulterode +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +NA +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +bwareaopen +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +areaOpening +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Remove small regions in binary or label image. + See also attributeOpening +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:areaOpening" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-IPT-Morphology-Binary" + +\end_inset + +Correspondencies from Image Processing Toolbox for morphological operations on +\series bold +binary +\series default + images +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +label2rgb +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +label2rgb +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Colorize a label image. +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:label2rgb" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-IPT-Morphology-Label" + +\end_inset + +Correspondencies from Image Processing Toolbox for morphological operations on +\series bold +label +\series default + images +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Newpage clearpage +\end_inset + + +\end_layout + +\begin_layout Subsection +Segmentation +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-IPT-Segmentation" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + provides correspondencies between image processing toolbox and Image-class functions related to segmentation of images. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imbinarize +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +binarize +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Binarization" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +graythresh +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +otsuThresholdValue +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "subsec:Binarization" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +watershed +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +watershed +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:watershed" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondences-IPT-Segmentation" + +\end_inset + +Correspondencies from Image Processing Toolbox for segmentation operations. +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Section +Image analysis +\end_layout + +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Correspondences-IPT-Segmentation" +plural "false" +caps "false" +noprefix "false" +nolink "false" + +\end_inset + + provides correspondencies between image processing toolbox and Image-class functions related to image analysis. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement document +alignment document +wide false +sideways false +status open + +\begin_layout Plain Layout +\begin_inset Tabular + + + + + + + + +\begin_inset Text + +\begin_layout Plain Layout +IPT +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Image class +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Notes +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +Page +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +imhist +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +histogram +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:histogram" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +regionprops +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +analyzeRegions +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "op:analyzeRegions" +nolink "false" + +\end_inset + + +\end_layout + +\end_inset + + + + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "tab:Correspondencies-IPT-Analysis" + +\end_inset + +Correspondencies from Image Processing Toolbox for image analysis +\end_layout + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset CommandInset index_print +LatexCommand printindex +type "idx" +name "General Index" + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset CommandInset index_print +LatexCommand printindex +type "met" +name "Image Class Methods" +literal "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset CommandInset bibtex +LatexCommand bibtex +btprint "btPrintCited" +bibfiles "imageClass-Manual" +options "bibtotoc,apalike" +encoding "default" + +\end_inset + + +\end_layout + +\end_body +\end_document diff --git a/doc/userManual/imageClass-Manual.bib b/doc/userManual/imageClass-Manual.bib new file mode 100644 index 0000000..0dcafe5 --- /dev/null +++ b/doc/userManual/imageClass-Manual.bib @@ -0,0 +1,89 @@ +@Book{Ohser_2009, + author = {Joachim Ohser and Katja Schladitz}, + publisher = {WILEY-VCH Verlag GmbH \& Co. KGaA, Weinheim}, + title = {{3D} images of materials structures}, + year = {2009}, + creationdate = {2025-09-04T11:09:15}, + groups = {Minkowski, Porous Media}, + owner = {David Legland}, + timestamp = {2013.01.09}, +} + +@InCollection{Schladitz_2006_DGCI, + author = {Schladitz, Katja and Ohser, Joachim and Nagel, Werner}, + booktitle = {Discrete Geometry for Computer Imagery}, + publisher = {Springer Berlin Heidelberg}, + title = {Measuring intrinsic volumes in digital 3d images}, + year = {2006}, + editor = {Kuba, Attila and Ny{{\'u}}l, L{{\'a}}szl{{\'o}} G. and Pal{{\'a}}gyi, K{{\'a}}lm{{\'a}}n}, + isbn = {978-3-540-47651-1}, + pages = {247-258}, + series = {Lecture Notes in Computer Science}, + volume = {4245}, + creationdate = {2025-09-04T11:09:15}, + doi = {10.1007/11907350_21}, + file = {:Schladitz_2006_DGCI.pdf:PDF}, + language = {English}, + owner = {dlegland}, + timestamp = {2015.04.17}, + url = {http://dx.doi.org/10.1007/11907350_21}, +} + +@Article{Lantuejoul_1981_JMicrosc, + author = {Lantu{\'{e}}joul, C. and Beucher, S.}, + journal = {Journal of Microscopy}, + title = {On the use of geodesic metric in image analysis}, + year = {1981}, + month = {Jan.}, + number = {1}, + pages = {39--49}, + volume = {121}, + creationdate = {2025-09-04T11:39:20}, + file = {:Lantuejoul_1981_JMicrosc.pdf:PDF}, + groups = {Geod. Diam, Tortuosity}, + timestamp = {2000.00.00}, + url = {http://dx.doi.org/10.1111/j.1365-2818.1981.tb01197.x}, +} + +@Book{Soille_2003, + author = {Soille, Pierre}, + publisher = {Springer}, + title = {{Morphological Image Analysis}}, + year = {2003}, + edition = {2nd}, + creationdate = {2025-09-04T11:43:07}, + groups = {References, Mathematical Morphology, Books}, + keywords = {mathematical morphology}, + timestamp = {2021.10.25}, + url = {http://ams.jrc.it/soille/book2nd}, +} + +@Article{Borgefors_1984_CVGIP, + author = {Borgefors, G.}, + journal = {Computer Vision, Graphics, and Image Processing}, + title = {Distance transformation in arbitrary dimensions.}, + year = {1984}, + pages = {321-145}, + volume = {27}, + creationdate = {2025-09-04T11:43:43}, + file = {:Borgefors_1984_CVGIP.pdf:PDF}, + groups = {Distance transform}, + owner = {David Legland}, + timestamp = {2010.09.07}, +} + +@Article{Borgefors_1986_CVGIP, + author = {Borgefors, G.}, + journal = {Computer Vision, Graphics, and Image Processing}, + title = {Distance transformations in digital images.}, + year = {1986}, + pages = {344-371}, + volume = {34}, + creationdate = {2025-09-04T11:43:43}, + file = {:Borgefors_1986_CVGIP.pdf:PDF}, + groups = {Distance transform}, + owner = {David Legland}, + timestamp = {2010.09.07}, +} + +@Comment{jabref-meta: databaseType:bibtex;} diff --git a/doc/userManual/images-src/binary/circles-boundary-ovr.png b/doc/userManual/images-src/binary/circles-boundary-ovr.png new file mode 100644 index 0000000..af96814 Binary files /dev/null and b/doc/userManual/images-src/binary/circles-boundary-ovr.png differ diff --git a/doc/userManual/images-src/binary/circles-distmap-rgb.png b/doc/userManual/images-src/binary/circles-distmap-rgb.png new file mode 100644 index 0000000..c9d6a54 Binary files /dev/null and b/doc/userManual/images-src/binary/circles-distmap-rgb.png differ diff --git a/doc/userManual/images-src/binary/circles-geodDistMap.png b/doc/userManual/images-src/binary/circles-geodDistMap.png new file mode 100644 index 0000000..6a43687 Binary files /dev/null and b/doc/userManual/images-src/binary/circles-geodDistMap.png differ diff --git a/doc/userManual/images-src/binary/circles-markerOvr.png b/doc/userManual/images-src/binary/circles-markerOvr.png new file mode 100644 index 0000000..49cb792 Binary files /dev/null and b/doc/userManual/images-src/binary/circles-markerOvr.png differ diff --git a/doc/userManual/images-src/binary/circles-skeleton-ovr.png b/doc/userManual/images-src/binary/circles-skeleton-ovr.png new file mode 100644 index 0000000..1d51d32 Binary files /dev/null and b/doc/userManual/images-src/binary/circles-skeleton-ovr.png differ diff --git a/doc/userManual/images-src/binary/demo_binary_images.m b/doc/userManual/images-src/binary/demo_binary_images.m new file mode 100644 index 0000000..7897ba6 --- /dev/null +++ b/doc/userManual/images-src/binary/demo_binary_images.m @@ -0,0 +1,65 @@ +%DEMO_BINARY_IMAGES One-line description here, please. +% +% output = demo_binary_images(input) +% +% Example +% demo_binary_images +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-01-14, using Matlab 9.7.0.1247435 (R2019b) Update 2 +% Copyright 2020 INRAE. + + +%% Distance map + +img = Image.read('circles.png'); + +distmap = distanceMap(img); +figure; show(distmap); colormap parula; + +% convert to RGB +distmap(~img) = NaN; +distmapRGB = double2rgb(distmap, 'parula', [], 'w'); + +write(distmapRGB, 'circles-distmap-rgb.png', 'png'); + + +%% Skeleton + +ovr = overlay(img, skeleton(img)); + +write(ovr, 'circles-skeleton-ovr.png', 'png'); + + +%% Boundary + +bnd = boundary(img); +ovr = overlay(img, dilation(bnd, ones(3,3)), 'r'); +write(ovr, 'circles-boundary-ovr.png', 'png'); + + +%% Geodesic distance map + +% create marker image +marker = Image.false(size(img)); +marker(80, 80) = 1; + +% save image of marker +markerV = dilation(marker, ones([3 7])); +markerH = dilation(marker, ones([7 3])); +crossMarker = markerV | markerH; +ovr = overlay(img, crossMarker, 'r'); +write(ovr, 'circles-markerOvr.png', 'png'); + +% compute using quasi-enclidean weights +distMap = geodesicDistanceMap(marker, img); +rgb = double2rgb(distMap, 'parula', [], 'w'); +rgb = overlay(rgb, crossMarker, 'r'); + +write(rgb, 'circles-geodDistMap.png', 'png'); diff --git a/doc/userManual/images-src/calib/image_coordinate_system.odg b/doc/userManual/images-src/calib/image_coordinate_system.odg new file mode 100644 index 0000000..b5e1e27 Binary files /dev/null and b/doc/userManual/images-src/calib/image_coordinate_system.odg differ diff --git a/doc/userManual/images-src/calib/image_coordinate_system.png b/doc/userManual/images-src/calib/image_coordinate_system.png new file mode 100644 index 0000000..54240ad Binary files /dev/null and b/doc/userManual/images-src/calib/image_coordinate_system.png differ diff --git a/doc/userManual/images-src/label/coins-bin.png b/doc/userManual/images-src/label/coins-bin.png new file mode 100644 index 0000000..67fa390 Binary files /dev/null and b/doc/userManual/images-src/label/coins-bin.png differ diff --git a/doc/userManual/images-src/label/coins-componentLabeling-gray8.png b/doc/userManual/images-src/label/coins-componentLabeling-gray8.png new file mode 100644 index 0000000..238b5cf Binary files /dev/null and b/doc/userManual/images-src/label/coins-componentLabeling-gray8.png differ diff --git a/doc/userManual/images-src/label/coins-componentLabeling-rgb.png b/doc/userManual/images-src/label/coins-componentLabeling-rgb.png new file mode 100644 index 0000000..ec580e7 Binary files /dev/null and b/doc/userManual/images-src/label/coins-componentLabeling-rgb.png differ diff --git a/doc/userManual/images-src/label/coins_componentsLabeling.m b/doc/userManual/images-src/label/coins_componentsLabeling.m new file mode 100644 index 0000000..bcdd5f1 --- /dev/null +++ b/doc/userManual/images-src/label/coins_componentsLabeling.m @@ -0,0 +1,32 @@ +%COINS_COMPONENTSLABELING One-line description here, please. +% +% output = coins_componentsLabeling(input) +% +% Example +% coins_componentsLabeling +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +% read data +img = Image.read('coins.png'); + +% binarise +bin = opening(img > 80, ones(3, 3)); +write(invert(bin), 'coins-bin.png', 'png'); + +% compute labels +lbl = componentLabeling(bin); +lbl8 = adjustDynamic(lbl); +write(lbl8, 'coins-componentLabeling-gray8.png', 'png'); + +% create color image +rgb = label2rgb(lbl, 'jet', 'w'); +write(rgb, 'coins-componentLabeling-rgb.png', 'png'); + diff --git a/doc/userManual/images-src/process/cameraman-circshift.png b/doc/userManual/images-src/process/cameraman-circshift.png new file mode 100644 index 0000000..f561f48 Binary files /dev/null and b/doc/userManual/images-src/process/cameraman-circshift.png differ diff --git a/doc/userManual/images-src/process/cameraman-concat.png b/doc/userManual/images-src/process/cameraman-concat.png new file mode 100644 index 0000000..1f9a57f Binary files /dev/null and b/doc/userManual/images-src/process/cameraman-concat.png differ diff --git a/doc/userManual/images-src/process/cameraman-flip.png b/doc/userManual/images-src/process/cameraman-flip.png new file mode 100644 index 0000000..9334d5a Binary files /dev/null and b/doc/userManual/images-src/process/cameraman-flip.png differ diff --git a/doc/userManual/images-src/process/cameraman-rot30.png b/doc/userManual/images-src/process/cameraman-rot30.png new file mode 100644 index 0000000..114a870 Binary files /dev/null and b/doc/userManual/images-src/process/cameraman-rot30.png differ diff --git a/doc/userManual/images-src/process/cameraman-rot90.png b/doc/userManual/images-src/process/cameraman-rot90.png new file mode 100644 index 0000000..f15198f Binary files /dev/null and b/doc/userManual/images-src/process/cameraman-rot90.png differ diff --git a/doc/userManual/images-src/process/cameraman-sub2.png b/doc/userManual/images-src/process/cameraman-sub2.png new file mode 100644 index 0000000..37acf3f Binary files /dev/null and b/doc/userManual/images-src/process/cameraman-sub2.png differ diff --git a/doc/userManual/images-src/process/coins_gradientNorm.png b/doc/userManual/images-src/process/coins_gradientNorm.png new file mode 100644 index 0000000..b7ebc4f Binary files /dev/null and b/doc/userManual/images-src/process/coins_gradientNorm.png differ diff --git a/doc/userManual/images-src/process/coins_gradientX.png b/doc/userManual/images-src/process/coins_gradientX.png new file mode 100644 index 0000000..8baaaa4 Binary files /dev/null and b/doc/userManual/images-src/process/coins_gradientX.png differ diff --git a/doc/userManual/images-src/process/coins_gradientY.png b/doc/userManual/images-src/process/coins_gradientY.png new file mode 100644 index 0000000..54b23cf Binary files /dev/null and b/doc/userManual/images-src/process/coins_gradientY.png differ diff --git a/doc/userManual/images-src/process/demoGeometryOperators.m b/doc/userManual/images-src/process/demoGeometryOperators.m new file mode 100644 index 0000000..201a5e5 --- /dev/null +++ b/doc/userManual/images-src/process/demoGeometryOperators.m @@ -0,0 +1,43 @@ +%DEMOGEOMETRYOPERATORS One-line description here, please. +% +% output = demoGeometryOperators(input) +% +% Example +% demoGeometryOperators +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-12-07, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2020 INRAE. + + +img = Image.read('cameraman.tif'); + +% flip in the x-direction +imgFlip = flip(img, 1); +write(imgFlip, 'cameraman-flip.png'); + +% rotation by 90 degrees +imgRot = rotate90(img, 1); +write(imgRot, 'cameraman-rot90.png'); + +% sub-sampling by 2 +imgSub = subsample(img, 2); +write(imgSub, 'cameraman-sub2.png'); + +% rotation +imgRot = rotate(img, 30); +write(imgRot, 'cameraman-rot30.png'); + +% circshift +imgShift = circshift(img, [150 100]); +write(imgShift, 'cameraman-circshift.png'); + +% Concatenation +imgCat = [img img img ; img img img]; +write(imgCat, 'cameraman-concat.png'); diff --git a/doc/userManual/images-src/process/demoGradientFilter_coins.m b/doc/userManual/images-src/process/demoGradientFilter_coins.m new file mode 100644 index 0000000..240796e --- /dev/null +++ b/doc/userManual/images-src/process/demoGradientFilter_coins.m @@ -0,0 +1,29 @@ +%Image Gradient demo +% +% output = demoGradientFilter_coins(input) +% +% Example +% demoGradientFilter_coins +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2018-08-08, using Matlab 9.4.0.813654 (R2018a) +% Copyright 2018 INRA - Cepia Software Platform. + +img = Image.read('coins.png'); +grad = norm(gradient(img)); +figure; show(grad, []); +[gx, gy] = gradient(img); +figure; subplot(121); show(gx,[]); subplot(122); show(gy,[]); + +write(uint8(grad*5), 'coins_gradientNorm.png'); + +gx8 = uint8((gx * 4 + 128)); +write(gx8, 'coins_gradientX.png'); + +gy8 = uint8((gy * 4 + 128)); +write(gy8, 'coins_gradientY.png'); diff --git a/doc/userManual/images-src/process/demoGradientFilter_rice.m b/doc/userManual/images-src/process/demoGradientFilter_rice.m new file mode 100644 index 0000000..3af3cca --- /dev/null +++ b/doc/userManual/images-src/process/demoGradientFilter_rice.m @@ -0,0 +1,29 @@ +%DEMOGRADIENTFILTER_RICE One-line description here, please. +% +% output = demoGradientFilter_rice(input) +% +% Example +% demoGradientFilter_rice +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2018-08-08, using Matlab 9.4.0.813654 (R2018a) +% Copyright 2018 INRA - Cepia Software Platform. + +img = Image.read('rice.png'); +grad = norm(gradient(img)); +figure; show(grad, []); +[gx, gy] = gradient(img); +figure; subplot(121); show(gx,[]); subplot(122); show(gy,[]); + +write(uint8(grad*5), 'rice_gradientNorm.png'); + +gx8 = uint8((gx * 4 + 128)); +write(gx8, 'rice_gradientX.png'); + +gy8 = uint8((gy * 4 + 128)); +write(gy8, 'rice_gradientY.png'); diff --git a/doc/userManual/images-src/process/demo_adjustDynamic_pout.m b/doc/userManual/images-src/process/demo_adjustDynamic_pout.m new file mode 100644 index 0000000..5f371d9 --- /dev/null +++ b/doc/userManual/images-src/process/demo_adjustDynamic_pout.m @@ -0,0 +1,33 @@ +%DEMO_ADJUSTDYNAMIC_POUT One-line description here, please. +% +% output = demo_adjustDynamic_pout(input) +% +% Example +% demo_adjustDynamic_pout +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2018-10-11, using Matlab 9.5.0.944444 (R2018b) +% Copyright 2018 INRA - Cepia Software Platform. + +% read image +img = Image.read('pout.tif'); + +% computes enhanced image +img2 = adjustDynamic(img, .01); +img2.name = 'pout-adjusted'; + +% write image +write(img2, 'pout_adjusted.tif'); + +% display histograms +figure; +subplot(211); histogram(img); +subplot(212); histogram(img2); + +% save figure +print(gcf, 'pout_histograms.png', '-dpng'); diff --git a/doc/userManual/images-src/process/demo_filter_rice.m b/doc/userManual/images-src/process/demo_filter_rice.m new file mode 100644 index 0000000..7a4f612 --- /dev/null +++ b/doc/userManual/images-src/process/demo_filter_rice.m @@ -0,0 +1,34 @@ +%DEMO_FILTER_RICE One-line description here, please. +% +% output = demo_filter_rice(input) +% +% Example +% demo_filter_rice +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +img = Image.read('rice.png'); + +% box filter +imgBox = boxFilter(img, [5 5]); +write(imgBox, 'rice-boxFilter5x5.png', 'png'); + +% box filter +imgBox = boxFilter(img, [5 5]); +write(imgBox, 'rice-boxFilter5x5.png', 'png'); + +% gaussian filter +imgGauss = gaussianFilter(img, [5 5], 2); +write(imgGauss, 'rice-gaussFilter5x5s2.png', 'png'); + +% median +imgMed = medianFilter(img, ones([5 5])); +write(imgMed, 'rice-medFilter5x5.png', 'png'); + diff --git a/doc/userManual/images-src/process/pout.tif b/doc/userManual/images-src/process/pout.tif new file mode 100644 index 0000000..0f8d7ad Binary files /dev/null and b/doc/userManual/images-src/process/pout.tif differ diff --git a/doc/userManual/images-src/process/pout_adjusted.tif b/doc/userManual/images-src/process/pout_adjusted.tif new file mode 100644 index 0000000..833e89e Binary files /dev/null and b/doc/userManual/images-src/process/pout_adjusted.tif differ diff --git a/doc/userManual/images-src/process/pout_histograms.png b/doc/userManual/images-src/process/pout_histograms.png new file mode 100644 index 0000000..57b3b86 Binary files /dev/null and b/doc/userManual/images-src/process/pout_histograms.png differ diff --git a/doc/userManual/images-src/process/rice-boxFilter5x5.png b/doc/userManual/images-src/process/rice-boxFilter5x5.png new file mode 100644 index 0000000..2d77977 Binary files /dev/null and b/doc/userManual/images-src/process/rice-boxFilter5x5.png differ diff --git a/doc/userManual/images-src/process/rice-gaussFilter5x5s2.png b/doc/userManual/images-src/process/rice-gaussFilter5x5s2.png new file mode 100644 index 0000000..951cdbf Binary files /dev/null and b/doc/userManual/images-src/process/rice-gaussFilter5x5s2.png differ diff --git a/doc/userManual/images-src/process/rice-medFilter5x5.png b/doc/userManual/images-src/process/rice-medFilter5x5.png new file mode 100644 index 0000000..a6773fc Binary files /dev/null and b/doc/userManual/images-src/process/rice-medFilter5x5.png differ diff --git a/doc/userManual/images-src/process/rice_gradientNorm.png b/doc/userManual/images-src/process/rice_gradientNorm.png new file mode 100644 index 0000000..a622dc6 Binary files /dev/null and b/doc/userManual/images-src/process/rice_gradientNorm.png differ diff --git a/doc/userManual/images-src/process/rice_gradientX.png b/doc/userManual/images-src/process/rice_gradientX.png new file mode 100644 index 0000000..5423569 Binary files /dev/null and b/doc/userManual/images-src/process/rice_gradientX.png differ diff --git a/doc/userManual/images-src/process/rice_gradientY.png b/doc/userManual/images-src/process/rice_gradientY.png new file mode 100644 index 0000000..a93dc57 Binary files /dev/null and b/doc/userManual/images-src/process/rice_gradientY.png differ diff --git a/doc/userManual/images-src/segment/coins-segMan-ovr.png b/doc/userManual/images-src/segment/coins-segMan-ovr.png new file mode 100644 index 0000000..36fd159 Binary files /dev/null and b/doc/userManual/images-src/segment/coins-segMan-ovr.png differ diff --git a/doc/userManual/images-src/segment/coins-segMan.png b/doc/userManual/images-src/segment/coins-segMan.png new file mode 100644 index 0000000..94a0a3f Binary files /dev/null and b/doc/userManual/images-src/segment/coins-segMan.png differ diff --git a/doc/userManual/images-src/segment/coins-segOtsu-bnd-ovr.png b/doc/userManual/images-src/segment/coins-segOtsu-bnd-ovr.png new file mode 100644 index 0000000..00dba15 Binary files /dev/null and b/doc/userManual/images-src/segment/coins-segOtsu-bnd-ovr.png differ diff --git a/doc/userManual/images-src/segment/coins-segOtsu-ovr.png b/doc/userManual/images-src/segment/coins-segOtsu-ovr.png new file mode 100644 index 0000000..6b1f62f Binary files /dev/null and b/doc/userManual/images-src/segment/coins-segOtsu-ovr.png differ diff --git a/doc/userManual/images-src/segment/coins-segWat-ovr.png b/doc/userManual/images-src/segment/coins-segWat-ovr.png new file mode 100644 index 0000000..029ef41 Binary files /dev/null and b/doc/userManual/images-src/segment/coins-segWat-ovr.png differ diff --git a/doc/userManual/images-src/segment/coins-segWat-rgb.png b/doc/userManual/images-src/segment/coins-segWat-rgb.png new file mode 100644 index 0000000..70330a9 Binary files /dev/null and b/doc/userManual/images-src/segment/coins-segWat-rgb.png differ diff --git a/doc/userManual/images-src/segment/segment_coins_watershed_Otsu.m b/doc/userManual/images-src/segment/segment_coins_watershed_Otsu.m new file mode 100644 index 0000000..9438943 --- /dev/null +++ b/doc/userManual/images-src/segment/segment_coins_watershed_Otsu.m @@ -0,0 +1,47 @@ +%OVERVIEW_SEGMENTRICEGRAINS One-line description here, please. +% +% output = overview_segmentRiceGrains(input) +% +% Example +% overview_segmentRiceGrains +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +% read data +img = Image.read('coins.png'); + +%% Manual segmentation + +segManual = img > 80; +write(uint8(segManual) * 255, 'coins-segMan.png', 'png'); +ovrManual = overlay(img, segManual, 'r'); +write(ovrManual, 'coins-segMan-ovr.png', 'png'); + + +%% segment using Otsu +segOtsu = otsuThreshold(img); +figure; show(segOtsu); +ovrOtsu = overlay(img, segOtsu, 'r'); +write(ovrOtsu, 'coins-segOtsu-ovr.png', 'png'); +ovrBndOtsu = overlay(img, boundary(segOtsu), 'g'); +write(ovrBndOtsu, 'coins-segOtsu-bnd-ovr.png', 'png'); + +%% segment using watershed on Gradient image +grad = norm(gradient(img)); +gradf = boxFilter(grad, [5 5]); +emin = extendedMinima(gradf, 20, 4); +grad2 = imposeMinima(gradf, emin, 4); +wat = watershed(grad2, 4); +ovrWat = overlay(img, wat==0, 'g'); +write(ovrWat, 'coins-segWat-ovr.png', 'png'); + +rgb = label2rgb(wat, 'jet', 'w'); +figure; show(rgb); +write(rgb, 'coins-segWat-rgb.png', 'png'); \ No newline at end of file diff --git a/doc/userManual/images-src/tour/cameraman-histogram.png b/doc/userManual/images-src/tour/cameraman-histogram.png new file mode 100644 index 0000000..d980116 Binary files /dev/null and b/doc/userManual/images-src/tour/cameraman-histogram.png differ diff --git a/doc/userManual/images-src/tour/cameraman-lineProfile.png b/doc/userManual/images-src/tour/cameraman-lineProfile.png new file mode 100644 index 0000000..ffb82ed Binary files /dev/null and b/doc/userManual/images-src/tour/cameraman-lineProfile.png differ diff --git a/doc/userManual/images-src/tour/cameraman-show.png b/doc/userManual/images-src/tour/cameraman-show.png new file mode 100644 index 0000000..c3b786f Binary files /dev/null and b/doc/userManual/images-src/tour/cameraman-show.png differ diff --git a/doc/userManual/images-src/tour/coins-grad-filt.png b/doc/userManual/images-src/tour/coins-grad-filt.png new file mode 100644 index 0000000..09e6262 Binary files /dev/null and b/doc/userManual/images-src/tour/coins-grad-filt.png differ diff --git a/doc/userManual/images-src/tour/coins-segWat-ovr.png b/doc/userManual/images-src/tour/coins-segWat-ovr.png new file mode 100644 index 0000000..57ac078 Binary files /dev/null and b/doc/userManual/images-src/tour/coins-segWat-ovr.png differ diff --git a/doc/userManual/images-src/tour/coins-segWat-rgb.png b/doc/userManual/images-src/tour/coins-segWat-rgb.png new file mode 100644 index 0000000..96d8461 Binary files /dev/null and b/doc/userManual/images-src/tour/coins-segWat-rgb.png differ diff --git a/doc/userManual/images-src/tour/overview_grayscale.m b/doc/userManual/images-src/tour/overview_grayscale.m new file mode 100644 index 0000000..3a8e8af --- /dev/null +++ b/doc/userManual/images-src/tour/overview_grayscale.m @@ -0,0 +1,28 @@ +%OVERVIEW_GRAYSCALE One-line description here, please. +% +% output = overview_grayscale(input) +% +% Example +% overview_grayscale +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +% read and display image +img = Image.read('cameraman.tif'); +figure; show(img); +print(gcf, 'cameraman-show.png', '-dpng'); + +% show histogram +figure; histogram(img); +print(gcf, 'cameraman-histogram.png', '-dpng'); + +% plot horizontal line profile in the middle of image +lineProfile(img, [10 125], [250 125]); +print(gcf, 'cameraman-lineProfile.png', '-dpng'); diff --git a/doc/userManual/images-src/tour/overview_riceGrains.m b/doc/userManual/images-src/tour/overview_riceGrains.m new file mode 100644 index 0000000..23eb0ad --- /dev/null +++ b/doc/userManual/images-src/tour/overview_riceGrains.m @@ -0,0 +1,41 @@ +%OVERVIEW_RICEGRAINS One-line description here, please. +% +% output = overview_riceGrains(input) +% +% Example +% overview_riceGrains +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +% read and explore data +img = Image.read('rice.png'); +figure; show(img); +lineProfile(img, [128 1], [128 255]); + +% filtering +img2 = medianFilter(img, ones([3 3])); +imgTop = whiteTopHat(img2, ones([30 30])); +figure; show(imgTop); + +% segmentation +figure; histogram(imgTop); +bin = imgTop > 50; + +% labeling +lbl = componentLabeling(bin); +lbl2 = killBorders(lbl); +rgb = label2rgb(lbl2); +figure; show(rgb); + +% analysis +centers = centroid(lbl2); +figure; show(img); hold on; +plot(centers(:,1), centers(:,2), 'r+'); + diff --git a/doc/userManual/images-src/tour/overview_segmentCoins.m b/doc/userManual/images-src/tour/overview_segmentCoins.m new file mode 100644 index 0000000..04d389d --- /dev/null +++ b/doc/userManual/images-src/tour/overview_segmentCoins.m @@ -0,0 +1,39 @@ +%OVERVIEW_SEGMENTRICEGRAINS One-line description here, please. +% +% output = overview_segmentRiceGrains(input) +% +% Example +% overview_segmentRiceGrains +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +img = Image.read('coins.png'); + +grad = norm(gradient(img)); +gradf = boxFilter(grad, [5 5]); +figure; show(gradf, []); +write(adjustDynamic(gradf), 'coins-grad-filt.png', 'png'); + +emin = extendedMinima(gradf, 20, 4); +grad2 = imposeMinima(gradf, emin, 4); +wat = watershed(grad2, 4); + +ovr = overlay(img, wat==0, 'g'); +figure; show(ovr); +write(ovr, 'coins-segWat-ovr.png', 'png'); + +wat2 = killBorders(wat); +rgb = label2rgb(wat2, 'jet', 'w'); +figure; show(rgb); +write(rgb, 'coins-segWat-rgb.png', 'png'); + +pts = centroid(wat2); +hold on; plot(pts(:,1), pts(:,2), 'k*') +print(gcf, 'coins-segWat-rgb-centroids.png', '-dong'); \ No newline at end of file diff --git a/doc/userManual/images-src/tour/overview_segmentRiceGrains.m b/doc/userManual/images-src/tour/overview_segmentRiceGrains.m new file mode 100644 index 0000000..3a8ce84 --- /dev/null +++ b/doc/userManual/images-src/tour/overview_segmentRiceGrains.m @@ -0,0 +1,31 @@ +%OVERVIEW_SEGMENTRICEGRAINS One-line description here, please. +% +% output = overview_segmentRiceGrains(input) +% +% Example +% overview_segmentRiceGrains +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2019-08-22, using Matlab 9.6.0.1072779 (R2019a) +% Copyright 2019 INRA - Cepia Software Platform. + +img = Image.read('rice.png'); + +grad = norm(gradient(img)); +figure; show(grad, []); + +emin = extendedMinima(grad, 10, 4); +grad2 = imposeMinima(grad, emin, 4); +wat = watershed(grad2, 4); + +ovr = overlay(img, wat==0, 'g'); +figure; show(ovr); + +rgb = label2rgb(wat, 'jet', 'w'); +figure(show(rgb)); + diff --git a/doc/userManual/images-src/vizu/brainMRI_isosurface3d.png b/doc/userManual/images-src/vizu/brainMRI_isosurface3d.png new file mode 100644 index 0000000..32c98e1 Binary files /dev/null and b/doc/userManual/images-src/vizu/brainMRI_isosurface3d.png differ diff --git a/doc/userManual/images-src/vizu/brainMRI_orthoPlanes.png b/doc/userManual/images-src/vizu/brainMRI_orthoPlanes.png new file mode 100644 index 0000000..93c6cfd Binary files /dev/null and b/doc/userManual/images-src/vizu/brainMRI_orthoPlanes.png differ diff --git a/doc/userManual/images-src/vizu/brainMRI_orthoSlices3d.png b/doc/userManual/images-src/vizu/brainMRI_orthoSlices3d.png new file mode 100644 index 0000000..9f7cdf7 Binary files /dev/null and b/doc/userManual/images-src/vizu/brainMRI_orthoSlices3d.png differ diff --git a/doc/userManual/images-src/vizu/demoHistogramRGB.m b/doc/userManual/images-src/vizu/demoHistogramRGB.m new file mode 100644 index 0000000..6d283f4 --- /dev/null +++ b/doc/userManual/images-src/vizu/demoHistogramRGB.m @@ -0,0 +1,36 @@ +%DEMOHISTOGRAMRGB One-line description here, please. +% +% output = demoHistogramRGB(input) +% +% Example +% demoHistogramRGB +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2018-10-08, using Matlab 9.4.0.813654 (R2018a) +% Copyright 2018 INRA - Cepia Software Platform. + +%% Read Color image + +img = Image.read('peppers.png'); +figure; show(img); +print(gcf, 'peppers-show.png', '-dpng'); + + +%% Compute histogram + +figure; histogram(img); +print(gcf, 'peppers-histogram.png', '-dpng'); + +%% Compute line profile + +lineProfile(img, [150 50], [150 300]); +print(gcf, 'peppers-lineProfile.png', '-dpng'); + +% overlay line profile on original image +img(149:151, 50:300, :) = 255; +write(img, 'peppers-lineOverlay.png'); \ No newline at end of file diff --git a/doc/userManual/images-src/vizu/demoVizu3D_mrihead.m b/doc/userManual/images-src/vizu/demoVizu3D_mrihead.m new file mode 100644 index 0000000..64c90f6 --- /dev/null +++ b/doc/userManual/images-src/vizu/demoVizu3D_mrihead.m @@ -0,0 +1,44 @@ +%DEMOVIZU3D_MRIHEAD One-line description here, please. +% +% output = demoVizu3D_mrihead(input) +% +% Example +% demoVizu3D_mrihead +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inra.fr +% Created: 2018-08-09, using Matlab 9.4.0.813654 (R2018a) +% Copyright 2018 INRA - Cepia Software Platform. + +% read data +img = Image.read('brainMRI.hdr'); +% setup spatial calibration +img.Spacing = [1 1 2.5]; + +% adjust histogram +img = adjustDynamic(img); + + +figure(1); clf; hold on; +showOrthoPlanes(img, [60 80 13]); +axis equal; % to have equal sizes +print(gcf, 'brainMRI_orthoPlanes.png', '-dpng'); + +figure(2); clf; hold on; +showOrthoSlices(img, [60 80 13]); +axis(physicalExtent(img)); % setup axis limits +axis equal; % to have equal sizes +view(3); +print(gcf, 'brainMRI_orthoSlices3d.png', '-dpng'); + + +figure(3); clf; hold on; +isosurface(gaussianFilter(img, [5 5 5], 2), 50); +axis(physicalExtent(img)); % setup axis limits +axis equal; % to have equal sizes +view([145 25]); light; +print(gcf, 'brainMRI_isosurface3d.png', '-dpng'); diff --git a/doc/userManual/images-src/vizu/peppers-histogram.png b/doc/userManual/images-src/vizu/peppers-histogram.png new file mode 100644 index 0000000..ecf0a3e Binary files /dev/null and b/doc/userManual/images-src/vizu/peppers-histogram.png differ diff --git a/doc/userManual/images-src/vizu/peppers-lineOverlay.png b/doc/userManual/images-src/vizu/peppers-lineOverlay.png new file mode 100644 index 0000000..701d41f Binary files /dev/null and b/doc/userManual/images-src/vizu/peppers-lineOverlay.png differ diff --git a/doc/userManual/images-src/vizu/peppers-lineProfile.png b/doc/userManual/images-src/vizu/peppers-lineProfile.png new file mode 100644 index 0000000..bb2beec Binary files /dev/null and b/doc/userManual/images-src/vizu/peppers-lineProfile.png differ diff --git a/doc/userManual/images-src/vizu/peppers-show.png b/doc/userManual/images-src/vizu/peppers-show.png new file mode 100644 index 0000000..941e14a Binary files /dev/null and b/doc/userManual/images-src/vizu/peppers-show.png differ diff --git a/doc/userManual/images/binary/circles-boundary-ovr.png b/doc/userManual/images/binary/circles-boundary-ovr.png new file mode 100644 index 0000000..da5a628 Binary files /dev/null and b/doc/userManual/images/binary/circles-boundary-ovr.png differ diff --git a/doc/userManual/images/binary/circles-distmap-rgb.png b/doc/userManual/images/binary/circles-distmap-rgb.png new file mode 100644 index 0000000..c9d6a54 Binary files /dev/null and b/doc/userManual/images/binary/circles-distmap-rgb.png differ diff --git a/doc/userManual/images/binary/circles-geodDistMap.png b/doc/userManual/images/binary/circles-geodDistMap.png new file mode 100644 index 0000000..1c4e24c Binary files /dev/null and b/doc/userManual/images/binary/circles-geodDistMap.png differ diff --git a/doc/userManual/images/binary/circles-markerOvr.png b/doc/userManual/images/binary/circles-markerOvr.png new file mode 100644 index 0000000..b8c9d86 Binary files /dev/null and b/doc/userManual/images/binary/circles-markerOvr.png differ diff --git a/doc/userManual/images/binary/circles-skeleton-ovr.png b/doc/userManual/images/binary/circles-skeleton-ovr.png new file mode 100644 index 0000000..be2366d Binary files /dev/null and b/doc/userManual/images/binary/circles-skeleton-ovr.png differ diff --git a/doc/userManual/images/calib/demoCrop.png b/doc/userManual/images/calib/demoCrop.png new file mode 100644 index 0000000..6492d46 Binary files /dev/null and b/doc/userManual/images/calib/demoCrop.png differ diff --git a/doc/userManual/images/calib/image_coordinate_system.png b/doc/userManual/images/calib/image_coordinate_system.png new file mode 100644 index 0000000..e3828dd Binary files /dev/null and b/doc/userManual/images/calib/image_coordinate_system.png differ diff --git a/doc/userManual/images/cameraman.png b/doc/userManual/images/cameraman.png new file mode 100644 index 0000000..1ac7d42 Binary files /dev/null and b/doc/userManual/images/cameraman.png differ diff --git a/doc/userManual/images/coins.png b/doc/userManual/images/coins.png new file mode 100644 index 0000000..4c12d37 Binary files /dev/null and b/doc/userManual/images/coins.png differ diff --git a/doc/userManual/images/dynamic/pout.png b/doc/userManual/images/dynamic/pout.png new file mode 100644 index 0000000..5ff171e Binary files /dev/null and b/doc/userManual/images/dynamic/pout.png differ diff --git a/doc/userManual/images/dynamic/pout.tif b/doc/userManual/images/dynamic/pout.tif new file mode 100644 index 0000000..0f8d7ad Binary files /dev/null and b/doc/userManual/images/dynamic/pout.tif differ diff --git a/doc/userManual/images/dynamic/pout_adjusted.png b/doc/userManual/images/dynamic/pout_adjusted.png new file mode 100644 index 0000000..f7a365b Binary files /dev/null and b/doc/userManual/images/dynamic/pout_adjusted.png differ diff --git a/doc/userManual/images/dynamic/pout_adjusted.tif b/doc/userManual/images/dynamic/pout_adjusted.tif new file mode 100644 index 0000000..833e89e Binary files /dev/null and b/doc/userManual/images/dynamic/pout_adjusted.tif differ diff --git a/doc/userManual/images/dynamic/pout_histograms.png b/doc/userManual/images/dynamic/pout_histograms.png new file mode 100644 index 0000000..57b3b86 Binary files /dev/null and b/doc/userManual/images/dynamic/pout_histograms.png differ diff --git a/doc/userManual/images/label/coins-bin.png b/doc/userManual/images/label/coins-bin.png new file mode 100644 index 0000000..6f74fc0 Binary files /dev/null and b/doc/userManual/images/label/coins-bin.png differ diff --git a/doc/userManual/images/label/coins-componentLabeling-gray8.png b/doc/userManual/images/label/coins-componentLabeling-gray8.png new file mode 100644 index 0000000..238b5cf Binary files /dev/null and b/doc/userManual/images/label/coins-componentLabeling-gray8.png differ diff --git a/doc/userManual/images/label/coins-componentLabeling-rgb.png b/doc/userManual/images/label/coins-componentLabeling-rgb.png new file mode 100644 index 0000000..696b750 Binary files /dev/null and b/doc/userManual/images/label/coins-componentLabeling-rgb.png differ diff --git a/doc/userManual/images/linearFilters/rice-boxFilter5x5.png b/doc/userManual/images/linearFilters/rice-boxFilter5x5.png new file mode 100644 index 0000000..2d77977 Binary files /dev/null and b/doc/userManual/images/linearFilters/rice-boxFilter5x5.png differ diff --git a/doc/userManual/images/linearFilters/rice-gaussFilter5x5s2.png b/doc/userManual/images/linearFilters/rice-gaussFilter5x5s2.png new file mode 100644 index 0000000..951cdbf Binary files /dev/null and b/doc/userManual/images/linearFilters/rice-gaussFilter5x5s2.png differ diff --git a/doc/userManual/images/linearFilters/rice-medFilter5x5.png b/doc/userManual/images/linearFilters/rice-medFilter5x5.png new file mode 100644 index 0000000..a6773fc Binary files /dev/null and b/doc/userManual/images/linearFilters/rice-medFilter5x5.png differ diff --git a/doc/userManual/images/linearFilters/rice_gradientNorm.png b/doc/userManual/images/linearFilters/rice_gradientNorm.png new file mode 100644 index 0000000..a622dc6 Binary files /dev/null and b/doc/userManual/images/linearFilters/rice_gradientNorm.png differ diff --git a/doc/userManual/images/linearFilters/rice_gradientX.png b/doc/userManual/images/linearFilters/rice_gradientX.png new file mode 100644 index 0000000..5423569 Binary files /dev/null and b/doc/userManual/images/linearFilters/rice_gradientX.png differ diff --git a/doc/userManual/images/linearFilters/rice_gradientY.png b/doc/userManual/images/linearFilters/rice_gradientY.png new file mode 100644 index 0000000..a93dc57 Binary files /dev/null and b/doc/userManual/images/linearFilters/rice_gradientY.png differ diff --git a/doc/userManual/images/morpho/rice-Closing-Sq2.png b/doc/userManual/images/morpho/rice-Closing-Sq2.png new file mode 100644 index 0000000..c574b53 Binary files /dev/null and b/doc/userManual/images/morpho/rice-Closing-Sq2.png differ diff --git a/doc/userManual/images/morpho/rice-Dilation-Sq2.png b/doc/userManual/images/morpho/rice-Dilation-Sq2.png new file mode 100644 index 0000000..848f70e Binary files /dev/null and b/doc/userManual/images/morpho/rice-Dilation-Sq2.png differ diff --git a/doc/userManual/images/morpho/rice-Erosion-Sq2.png b/doc/userManual/images/morpho/rice-Erosion-Sq2.png new file mode 100644 index 0000000..3ddd779 Binary files /dev/null and b/doc/userManual/images/morpho/rice-Erosion-Sq2.png differ diff --git a/doc/userManual/images/morpho/rice-Opening-Sq2.png b/doc/userManual/images/morpho/rice-Opening-Sq2.png new file mode 100644 index 0000000..a4a4863 Binary files /dev/null and b/doc/userManual/images/morpho/rice-Opening-Sq2.png differ diff --git a/doc/userManual/images/morpho/rice.png b/doc/userManual/images/morpho/rice.png new file mode 100644 index 0000000..b52edb6 Binary files /dev/null and b/doc/userManual/images/morpho/rice.png differ diff --git a/doc/userManual/images/morpho/wheatGrain_tomo_z630.tif b/doc/userManual/images/morpho/wheatGrain_tomo_z630.tif new file mode 100644 index 0000000..b306bd1 Binary files /dev/null and b/doc/userManual/images/morpho/wheatGrain_tomo_z630.tif differ diff --git a/doc/userManual/images/morpho/wheatGrain_tomo_z630_fillHoles.tif b/doc/userManual/images/morpho/wheatGrain_tomo_z630_fillHoles.tif new file mode 100644 index 0000000..8a28c24 Binary files /dev/null and b/doc/userManual/images/morpho/wheatGrain_tomo_z630_fillHoles.tif differ diff --git a/doc/userManual/images/morpho/wheatGrain_tomo_z630_killBorders.tif b/doc/userManual/images/morpho/wheatGrain_tomo_z630_killBorders.tif new file mode 100644 index 0000000..bc717d6 Binary files /dev/null and b/doc/userManual/images/morpho/wheatGrain_tomo_z630_killBorders.tif differ diff --git a/doc/userManual/images/overview/cameraman-histogram.png b/doc/userManual/images/overview/cameraman-histogram.png new file mode 100644 index 0000000..d980116 Binary files /dev/null and b/doc/userManual/images/overview/cameraman-histogram.png differ diff --git a/doc/userManual/images/overview/cameraman-lineProfile.png b/doc/userManual/images/overview/cameraman-lineProfile.png new file mode 100644 index 0000000..ffb82ed Binary files /dev/null and b/doc/userManual/images/overview/cameraman-lineProfile.png differ diff --git a/doc/userManual/images/overview/cameraman-show.png b/doc/userManual/images/overview/cameraman-show.png new file mode 100644 index 0000000..c3b786f Binary files /dev/null and b/doc/userManual/images/overview/cameraman-show.png differ diff --git a/doc/userManual/images/overview/coins-grad-filt.png b/doc/userManual/images/overview/coins-grad-filt.png new file mode 100644 index 0000000..09e6262 Binary files /dev/null and b/doc/userManual/images/overview/coins-grad-filt.png differ diff --git a/doc/userManual/images/overview/coins-segWat-ovr.png b/doc/userManual/images/overview/coins-segWat-ovr.png new file mode 100644 index 0000000..57ac078 Binary files /dev/null and b/doc/userManual/images/overview/coins-segWat-ovr.png differ diff --git a/doc/userManual/images/overview/coins-segWat-rgb.png b/doc/userManual/images/overview/coins-segWat-rgb.png new file mode 100644 index 0000000..96d8461 Binary files /dev/null and b/doc/userManual/images/overview/coins-segWat-rgb.png differ diff --git a/doc/userManual/images/process/A01bis_crop.tif b/doc/userManual/images/process/A01bis_crop.tif new file mode 100644 index 0000000..d0a21ef Binary files /dev/null and b/doc/userManual/images/process/A01bis_crop.tif differ diff --git a/doc/userManual/images/process/A01cropO.png b/doc/userManual/images/process/A01cropO.png new file mode 100644 index 0000000..6e841cb Binary files /dev/null and b/doc/userManual/images/process/A01cropO.png differ diff --git a/doc/userManual/images/process/A01cropOC.png b/doc/userManual/images/process/A01cropOC.png new file mode 100644 index 0000000..6127db0 Binary files /dev/null and b/doc/userManual/images/process/A01cropOC.png differ diff --git a/doc/userManual/images/process/A01cropOCseg.png b/doc/userManual/images/process/A01cropOCseg.png new file mode 100644 index 0000000..52672b7 Binary files /dev/null and b/doc/userManual/images/process/A01cropOCseg.png differ diff --git a/doc/userManual/images/process/A01cropOCseg2.png b/doc/userManual/images/process/A01cropOCseg2.png new file mode 100644 index 0000000..2a0b8d2 Binary files /dev/null and b/doc/userManual/images/process/A01cropOCseg2.png differ diff --git a/doc/userManual/images/process/A01cropOCsegOvr.png b/doc/userManual/images/process/A01cropOCsegOvr.png new file mode 100644 index 0000000..dfbd54c Binary files /dev/null and b/doc/userManual/images/process/A01cropOCsegOvr.png differ diff --git a/doc/userManual/images/process/peppers_kmeans.m b/doc/userManual/images/process/peppers_kmeans.m new file mode 100644 index 0000000..62b72c0 --- /dev/null +++ b/doc/userManual/images/process/peppers_kmeans.m @@ -0,0 +1,32 @@ +%PEPPERS_KMEANS One-line description here, please. +% +% output = peppers_kmeans(input) +% +% Example +% peppers_kmeans +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-03-13, using Matlab 9.7.0.1247435 (R2019b) Update 2 +% Copyright 2020 INRAE. + +% read the data +img = Image.read('peppers.png'); + +% compute k-means classes +cls = kmeans(img, 6); + +% display the classes with arbitrary colors +rgb = label2rgb(cls, 'jet', 'w'); +write(rgb, 'peppers_kmeans6_classes.png'); + +% display the classes with average color of each region +[cls, centers] = kmeans(img, 6); +map = centers / 255; % average color of each class +rgb = label2rgb(cls, map); +write(rgb, 'peppers_kmeans6_averageColor.png'); diff --git a/doc/userManual/images/process/peppers_kmeans6_averageColor.png b/doc/userManual/images/process/peppers_kmeans6_averageColor.png new file mode 100644 index 0000000..dae8ee7 Binary files /dev/null and b/doc/userManual/images/process/peppers_kmeans6_averageColor.png differ diff --git a/doc/userManual/images/process/peppers_kmeans6_classes.png b/doc/userManual/images/process/peppers_kmeans6_classes.png new file mode 100644 index 0000000..eeb970d Binary files /dev/null and b/doc/userManual/images/process/peppers_kmeans6_classes.png differ diff --git a/doc/userManual/images/process/processMaizeCrop.m b/doc/userManual/images/process/processMaizeCrop.m new file mode 100644 index 0000000..4f04f4c --- /dev/null +++ b/doc/userManual/images/process/processMaizeCrop.m @@ -0,0 +1,35 @@ +%PROCESSMAIZECROP One-line description here, please. +% +% output = processMaizeCrop(input) +% +% Example +% processMaizeCrop +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-02-18, using Matlab 9.7.0.1247435 (R2019b) Update 2 +% Copyright 2020 INRAE. + +img = Image.read('A01bis_crop.tif'); + +imgO = opening(img, strel('disk', 5)); +write(imgO, 'A01cropO.png'); + +imgOC = closing(imgO, strel('disk', 10)); +write(imgOC, 'A01cropOC.png'); + +seg = otsuThreshold(imgOC); +write(seg, 'A01cropOCseg.png'); + +seg2 = areaOpening(seg, 1000); +write(seg2, 'A01cropOCseg2.png'); + +ovr = overlay(img, dilation(boundary(seg2), strel('disk', 4))); +write(ovr, 'A01cropOCsegOvr.png'); + +figure; show(ovr) \ No newline at end of file diff --git a/doc/userManual/images/process/process_rice.m b/doc/userManual/images/process/process_rice.m new file mode 100644 index 0000000..b3a9915 --- /dev/null +++ b/doc/userManual/images/process/process_rice.m @@ -0,0 +1,38 @@ +%PROCESS_RICE One-line description here, please. +% +% output = process_rice(input) +% +% Example +% process_rice +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-02-18, using Matlab 9.7.0.1247435 (R2019b) Update 2 +% Copyright 2020 INRAE. + +img = Image.read('rice.png'); + +img2 = medianFilter(img, ones(3, 3)); +write(img2, 'rice_med.tif'); + +img3 = whiteTopHat(img2, ones(30, 30)); +write(img3, 'rice_med_WTH.tif'); + +img4 = otsuThreshold(img3); +write(img4, 'rice_med_WTH_otsu.tif'); + +img5 = ~img4; +img5(:, [1 end]) = false; +img5([1 end], :) = false; +write(img5, 'rice_med_WTH_otsuInvB.tif'); + +img6 = componentLabeling(img4, 4); +write(img6, 'rice_med_WTH_otsu_lbl.tif'); + +rgb = label2rgb(img6, 'jet', 'w', 'shuffle'); +write(rgb, 'rice_med_WTH_otsu_lbl_rgb.tif'); diff --git a/doc/userManual/images/process/rice_med.tif b/doc/userManual/images/process/rice_med.tif new file mode 100644 index 0000000..0252a1b Binary files /dev/null and b/doc/userManual/images/process/rice_med.tif differ diff --git a/doc/userManual/images/process/rice_med_WTH.tif b/doc/userManual/images/process/rice_med_WTH.tif new file mode 100644 index 0000000..920d379 Binary files /dev/null and b/doc/userManual/images/process/rice_med_WTH.tif differ diff --git a/doc/userManual/images/process/rice_med_WTH_maxEnt.tif b/doc/userManual/images/process/rice_med_WTH_maxEnt.tif new file mode 100644 index 0000000..d342970 Binary files /dev/null and b/doc/userManual/images/process/rice_med_WTH_maxEnt.tif differ diff --git a/doc/userManual/images/process/rice_med_WTH_otsu.tif b/doc/userManual/images/process/rice_med_WTH_otsu.tif new file mode 100644 index 0000000..1dd47e3 Binary files /dev/null and b/doc/userManual/images/process/rice_med_WTH_otsu.tif differ diff --git a/doc/userManual/images/process/rice_med_WTH_otsuInvB.tif b/doc/userManual/images/process/rice_med_WTH_otsuInvB.tif new file mode 100644 index 0000000..bcb23cb Binary files /dev/null and b/doc/userManual/images/process/rice_med_WTH_otsuInvB.tif differ diff --git a/doc/userManual/images/process/rice_med_WTH_otsu_lbl.tif b/doc/userManual/images/process/rice_med_WTH_otsu_lbl.tif new file mode 100644 index 0000000..b8c6ea8 Binary files /dev/null and b/doc/userManual/images/process/rice_med_WTH_otsu_lbl.tif differ diff --git a/doc/userManual/images/process/rice_med_WTH_otsu_lbl_rgb.tif b/doc/userManual/images/process/rice_med_WTH_otsu_lbl_rgb.tif new file mode 100644 index 0000000..8906ebc Binary files /dev/null and b/doc/userManual/images/process/rice_med_WTH_otsu_lbl_rgb.tif differ diff --git a/doc/userManual/images/segment/coins-segMan-ovr.png b/doc/userManual/images/segment/coins-segMan-ovr.png new file mode 100644 index 0000000..36fd159 Binary files /dev/null and b/doc/userManual/images/segment/coins-segMan-ovr.png differ diff --git a/doc/userManual/images/segment/coins-segOtsu-ovr.png b/doc/userManual/images/segment/coins-segOtsu-ovr.png new file mode 100644 index 0000000..6b1f62f Binary files /dev/null and b/doc/userManual/images/segment/coins-segOtsu-ovr.png differ diff --git a/doc/userManual/images/segment/coins-segWat-rgb.png b/doc/userManual/images/segment/coins-segWat-rgb.png new file mode 100644 index 0000000..6787a34 Binary files /dev/null and b/doc/userManual/images/segment/coins-segWat-rgb.png differ diff --git a/doc/userManual/images/shape/cameraman-circshift.png b/doc/userManual/images/shape/cameraman-circshift.png new file mode 100644 index 0000000..4760442 Binary files /dev/null and b/doc/userManual/images/shape/cameraman-circshift.png differ diff --git a/doc/userManual/images/shape/cameraman-concat.png b/doc/userManual/images/shape/cameraman-concat.png new file mode 100644 index 0000000..1f9a57f Binary files /dev/null and b/doc/userManual/images/shape/cameraman-concat.png differ diff --git a/doc/userManual/images/shape/cameraman-flip.png b/doc/userManual/images/shape/cameraman-flip.png new file mode 100644 index 0000000..0dbf2a6 Binary files /dev/null and b/doc/userManual/images/shape/cameraman-flip.png differ diff --git a/doc/userManual/images/shape/cameraman-rot30.png b/doc/userManual/images/shape/cameraman-rot30.png new file mode 100644 index 0000000..53d6a12 Binary files /dev/null and b/doc/userManual/images/shape/cameraman-rot30.png differ diff --git a/doc/userManual/images/shape/cameraman-rot90.png b/doc/userManual/images/shape/cameraman-rot90.png new file mode 100644 index 0000000..90d57d2 Binary files /dev/null and b/doc/userManual/images/shape/cameraman-rot90.png differ diff --git a/doc/userManual/images/shape/wheatGrainCrop-boxes.tif.png b/doc/userManual/images/shape/wheatGrainCrop-boxes.tif.png new file mode 100644 index 0000000..eb5de52 Binary files /dev/null and b/doc/userManual/images/shape/wheatGrainCrop-boxes.tif.png differ diff --git a/doc/userManual/images/shape/wheatGrainCrop.tif b/doc/userManual/images/shape/wheatGrainCrop.tif new file mode 100644 index 0000000..73b002d Binary files /dev/null and b/doc/userManual/images/shape/wheatGrainCrop.tif differ diff --git a/doc/userManual/images/shape/wheatGrainCropOriented.tif b/doc/userManual/images/shape/wheatGrainCropOriented.tif new file mode 100644 index 0000000..d80285e Binary files /dev/null and b/doc/userManual/images/shape/wheatGrainCropOriented.tif differ diff --git a/doc/userManual/images/vizu/brainMRI_isosurface3d.png b/doc/userManual/images/vizu/brainMRI_isosurface3d.png new file mode 100644 index 0000000..32c98e1 Binary files /dev/null and b/doc/userManual/images/vizu/brainMRI_isosurface3d.png differ diff --git a/doc/userManual/images/vizu/brainMRI_orthoPlanes.png b/doc/userManual/images/vizu/brainMRI_orthoPlanes.png new file mode 100644 index 0000000..93c6cfd Binary files /dev/null and b/doc/userManual/images/vizu/brainMRI_orthoPlanes.png differ diff --git a/doc/userManual/images/vizu/brainMRI_orthoSlices3d.png b/doc/userManual/images/vizu/brainMRI_orthoSlices3d.png new file mode 100644 index 0000000..9f7cdf7 Binary files /dev/null and b/doc/userManual/images/vizu/brainMRI_orthoSlices3d.png differ diff --git a/doc/userManual/images/vizu/peppers-histogram.png b/doc/userManual/images/vizu/peppers-histogram.png new file mode 100644 index 0000000..ecf0a3e Binary files /dev/null and b/doc/userManual/images/vizu/peppers-histogram.png differ diff --git a/doc/userManual/images/vizu/peppers-lineOverlay.png b/doc/userManual/images/vizu/peppers-lineOverlay.png new file mode 100644 index 0000000..701d41f Binary files /dev/null and b/doc/userManual/images/vizu/peppers-lineOverlay.png differ diff --git a/doc/userManual/images/vizu/peppers-lineProfile.png b/doc/userManual/images/vizu/peppers-lineProfile.png new file mode 100644 index 0000000..bb2beec Binary files /dev/null and b/doc/userManual/images/vizu/peppers-lineProfile.png differ diff --git a/doc/userManual/images/vizu/peppers_JointHistogramRG_log_annotated.png b/doc/userManual/images/vizu/peppers_JointHistogramRG_log_annotated.png new file mode 100644 index 0000000..f8d209c Binary files /dev/null and b/doc/userManual/images/vizu/peppers_JointHistogramRG_log_annotated.png differ diff --git a/doc/userManual/images/wheatGrainSlice.tif b/doc/userManual/images/wheatGrainSlice.tif new file mode 100644 index 0000000..16f8acd Binary files /dev/null and b/doc/userManual/images/wheatGrainSlice.tif differ diff --git a/src/@Image/Image.m b/src/@Image/Image.m index 159b486..cf64457 100644 --- a/src/@Image/Image.m +++ b/src/@Image/Image.m @@ -108,14 +108,20 @@ methods(Access = protected) se = defaultStructuringElement(obj, varargin) + conn = defaultConnectivity(obj); name = createNewName(obj, pattern) end +%% Private utility methods +methods(Static, Access = protected) + weights = directionWeights3d13(delta); +end + %% Constructor declaration methods function obj = Image(varargin) - %IMAGE Constructor for Image object. + % Constructor for Image object. % % Syntax % IMG = Image(MAT); diff --git a/src/@Image/areaOpening.m b/src/@Image/areaOpening.m index 70605dc..bae03a0 100644 --- a/src/@Image/areaOpening.m +++ b/src/@Image/areaOpening.m @@ -47,11 +47,10 @@ elseif isBinaryImage(obj) % if image is binary compute labeling - % first determines connectivity to use - conn = 4; - if ndims(obj) == 3 - conn = 6; - end + % choose default connectivity depending on dimension + conn = defaultConnectivity(obj); + + % case of connectivity specified by user if ~isempty(varargin) conn = varargin{1}; end diff --git a/src/@Image/catChannels.m b/src/@Image/catChannels.m index 2579f6c..387f3da 100644 --- a/src/@Image/catChannels.m +++ b/src/@Image/catChannels.m @@ -7,16 +7,16 @@ % % 'manual' creation of a color image % img = Image.read('cameraman.tif'); % res = catChannels(img, img, invert(img)); -% res.type = 'color'; +% res.Type = 'color'; % show(res); % % See also -% splitChannels, cat, catFrames +% splitChannels, createRGB, cat, catFrames % % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2011-11-22, using Matlab 7.9.0.529 (R2009b) % Copyright 2011 INRA - Cepia Software Platform. @@ -31,7 +31,7 @@ end res = Image(... - 'data', data, ... - 'parent', obj, ... - 'type', 'vector', ... - 'name', name); + 'Data', data, ... + 'Parent', obj, ... + 'Type', 'vector', ... + 'Name', name); diff --git a/src/@Image/chamferDistanceMap.m b/src/@Image/chamferDistanceMap.m new file mode 100644 index 0000000..ad2f85a --- /dev/null +++ b/src/@Image/chamferDistanceMap.m @@ -0,0 +1,203 @@ +function map = chamferDistanceMap(obj, varargin) +% Distance map of a binary image computed using chamfer mask. +% +% DISTMAP = chamferDistanceMap(IMG) +% DISTMAP = chamferDistanceMap(IMG, WEIGHTS) +% Computes the distance map of the input image using chamfer weights. The +% aim of this function is similar to that of the "distanceMap" one, with +% the following specificities: +% * possibility to use 5-by-5 chamfer masks +% * possibility to compute distance maps for label images with touching +% regions. +% +% Example +% chamferDistanceMap +% +% See also +% distanceMap, geodesicDistanceMap +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + +%% Process input arguments + +% default weights for orthogonal or diagonal +weights = [5 7 11]; + +normalize = true; + +% extract user-specified weights +if ~isempty(varargin) + weights = varargin{1}; + varargin(1) = []; +end + +% extract verbosity option +verbose = false; +if length(varargin) > 1 + varName = varargin{1}; + if ~ischar(varName) + error('Require options as name-value pairs'); + end + + if strcmpi(varName, 'normalize') + normalize = varargin{2}; + elseif strcmpi(varName, 'verbose') + verbose = varargin{2}; + else + error(['unknown option: ' varName]); + end +end + + +%% Initialisations + +% determines type of output from type of weights +outputType = class(weights); + +% small check up to avoid degenerate cases +w1 = weights(1); +w2 = weights(2); +if w2 < w1 + w2 = 2 * w1; +end + +% shifts in directions i and j for (1) forward and (2) backward iterations +if length(weights) == 2 + nShifts = 4; + di1 = [-1 -1 -1 0]; + dj1 = [-1 0 1 -1]; + di2 = [+1 +1 +1 0]; + dj2 = [-1 0 1 +1]; + ws = [w2 w1 w2 w1]; + +elseif length(weights) == 3 + nShifts = 8; + w3 = weights(3); + di1 = [-2 -2 -1 -1 -1 -1 -1 0]; + dj1 = [-1 +1 -2 -1 0 1 +2 -1]; + di2 = [+2 +2 +1 +1 +1 +1 +1 0]; + dj2 = [-1 +1 +2 +1 0 -1 -2 +1]; + ws = [w3 w3 w3 w2 w1 w2 w3 w1]; +end + +% allocate memory for result +dist = ones(size(obj.Data), outputType); + +% init result: either max value, or 0 for marker pixels +if isinteger(w1) + dist(:) = intmax(outputType); +else + dist(:) = inf; +end +dist(obj.Data == 0) = 0; + +% size of image +[D1, D2] = size(obj.Data); + + +%% Forward iteration + +if verbose + disp('Forward iteration %d'); +end + +for i = 1:D1 + for j = 1:D2 + % computes only for pixels within a region + if obj.Data(i, j) == 0 + continue; + end + + % compute minimal propagated distance + newVal = dist(i, j); + for k = 1:nShifts + % coordinate of neighbor + i2 = i + di1(k); + j2 = j + dj1(k); + + % check bounds + if i2 < 1 || i2 > D1 || j2 < 1 || j2 > D2 + continue; + end + + % compute new value + if obj.Data(i2, j2) == obj.Data(i, j) + % neighbor in same region + % -> add offset weight to neighbor distance + newVal = min(newVal, dist(i2, j2) + ws(k)); + else + % neighbor in another region + % -> initialize with the offset weight + newVal = min(newVal, ws(k)); + end + end + + % if distance was changed, update result + dist(i,j) = newVal; + end + +end % iteration on lines + + + +%% Backward iteration + +if verbose + disp('Backward iteration'); +end + +for i = D1:-1:1 + for j = D2:-1:1 + % computes only for foreground pixels + if obj.Data(i, j) == 0 + continue; + end + + % compute minimal propagated distance + newVal = dist(i, j); + for k = 1:nShifts + % coordinate of neighbor + i2 = i + di2(k); + j2 = j + dj2(k); + + % check bounds + if i2 < 1 || i2 > D1 || j2 < 1 || j2 > D2 + continue; + end + + % compute new value + if obj.Data(i2, j2) == obj.Data(i, j) + % neighbor in same region + % -> add offset weight to neighbor distance + newVal = min(newVal, dist(i2, j2) + ws(k)); + else + % neighbor in another region + % -> initialize with the offset weight + newVal = min(newVal, ws(k)); + end + end + + % if distance was changed, update result + dist(i,j) = newVal; + end + +end % line iteration + +if normalize + dist(obj.Data>0) = dist(obj.Data>0) / w1; +end + +newName = createNewName(obj, '%s-distMap'); + +% create new image +map = Image('Data', dist, ... + 'Parent', obj, ... + 'Name', newName, ... + 'Type', 'intensity', ... + 'ChannelNames', {'distance'}); diff --git a/src/@Image/clone.m b/src/@Image/clone.m index 89c9dbc..0325ba8 100644 --- a/src/@Image/clone.m +++ b/src/@Image/clone.m @@ -1,17 +1,27 @@ function res = clone(obj) % Create a deep-copy of an Image object. % -% output = clone(input) +% RES = clone(IMG) +% Return a new Image, initialized with same data values as in IMG. +% % % Example -% clone +% img = Image.read('rice.png'); +% img2 = clone(img); +% all(size(img) == size(img2)) +% ans = +% 1 +% strcmp(class(img.Data), class(img2.Data)) +% ans = +% 1 % % See also +% read, zeros, ones % % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2011-12-15, using Matlab 7.9.0.529 (R2009b) % Copyright 2011 INRA - Cepia Software Platform. diff --git a/src/@Image/colorCloud.m b/src/@Image/colorCloud.m new file mode 100644 index 0000000..f8cb10d --- /dev/null +++ b/src/@Image/colorCloud.m @@ -0,0 +1,46 @@ +function varargout = colorCloud(obj, varargin) +% Display image colors into the 3D space of specified gamut. +% +% colorCloud(IMG) +% colorCloud(IMG, GAMUT) +% See the "colorcloud" function help for options. +% +% Works also for 3D images. +% +% Example +% % Read and display a color image +% img = Image.read('peppers.png'); +% figure; show(img); +% % Display color cloud in a new figure +% colorCloud(img) +% +% See also +% show, isColorImage, colorcloud +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-02-18, using Matlab 9.7.0.1247435 (R2019b) Update 2 +% Copyright 2020 INRAE. + +% check input +if ~isColorImage(obj) + error('requires a color image as input'); +end + +% format data +if obj.DataSize(3) == 1 + rgb = permute(obj.Data(:,:,1,:,1), [2 1 4 3 5]); +else + nSlices = obj.DataSize(3); + rgb = zeros([0 3]); + for iz = 1:nSlices + rgb = [rgb ; permute(obj.Data(:,:,iz,:,1), [2 1 4 3 5])]; %#ok + end +end + +% call the IPT function +varargout = cell(1, nargout); +[varargout{:}] = colorcloud(rgb, varargin{:}); diff --git a/src/@Image/componentLabeling.m b/src/@Image/componentLabeling.m index 82fca6a..6a3ca9c 100644 --- a/src/@Image/componentLabeling.m +++ b/src/@Image/componentLabeling.m @@ -12,7 +12,7 @@ % figure; show(rgb); % % See also -% label2rgb, watershed +% label2rgb, watershed, reconstruction % % ------ @@ -26,14 +26,22 @@ error('Requires a binary image'); end +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); + +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; +end + nd = ndims(obj); if nd == 2 % Planar images - data = bwlabel(obj.Data, varargin{:}); + data = bwlabel(obj.Data, conn); elseif nd == 3 % 3D images - data = bwlabeln(obj.Data, varargin{:}); + data = bwlabeln(obj.Data, conn); else error('Function "componentLabeling" is not implemented for image of dim %d', nd); diff --git a/src/@Image/create.m b/src/@Image/create.m index 1da1530..fd748ea 100644 --- a/src/@Image/create.m +++ b/src/@Image/create.m @@ -19,12 +19,12 @@ % % % See also -% read, ones, zeros +% read, ones, zeros, true, false % % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2010-07-21, using Matlab 7.9.0.529 (R2009b) % Copyright 2010 INRA - Cepia Software Platform. @@ -59,7 +59,7 @@ end % create empty data buffer - if strcmp(type, 'logical') || strcmp(type, 'binary') + if strcmpi(type, 'logical') || strcmp(type, 'binary') % case of binary image data = false(imageSize); else @@ -83,7 +83,7 @@ continue; end - if strcmp(varargin{i}, 'data') + if strcmpi(varargin{i}, 'data') data = varargin{i+1}; imageSize = size(data); varargin(i:i+1) = []; @@ -120,4 +120,4 @@ % call constructor depending on image dimension nd = length(imageSize); -img = Image(nd, 'data', data, arguments{:}); +img = Image(nd, 'Data', data, arguments{:}); diff --git a/src/@Image/createRGB.m b/src/@Image/createRGB.m index d6348b9..3945e4c 100644 --- a/src/@Image/createRGB.m +++ b/src/@Image/createRGB.m @@ -1,20 +1,22 @@ function rgb = createRGB(red, green, blue) % Create a RGB color image from scalar channels. % -% RGB = createRGB(DATA) +% RGB = Image.createRGB(DATA) % Create a RGB image from data array DATA, using the third dimension as % channel dimension. % Input array DATA can be either a 3D or a 4D array, resulting in a 2D or % 3D image. DATA is ordered using Matlab convention: y, x, channel, z. % -% RGB = createRGB(RED, GREEN, BLUE) +% RGB = Image.createRGB(RED, GREEN, BLUE) % Concatenates the 3 data arrays to form a RGB color image. Inputs can be % either 2D or 3D, but they must have the same dimension. One of them can % be empty. % % % Example -% createRGB +% img = Image.read('peppers.png'); +% img2 = Image.createRGB(channel(img, 3), channel(img, 1), channel(img, 2)); +% figure; show(img2) % % See also % create, Image, catChannels diff --git a/src/@Image/cropOrientedBox.m b/src/@Image/cropOrientedBox.m new file mode 100644 index 0000000..0f26fe3 --- /dev/null +++ b/src/@Image/cropOrientedBox.m @@ -0,0 +1,52 @@ +function res = cropOrientedBox(obj, obox, varargin) +% Crop the content of an image within an oriented box. +% +% RES = cropOrientedBox(IMG, OBOX) +% Crops the content of the image IMG that is contained within the +% oriented box OBOX. The size of the resulting image is approximately +% (due to rounding) the size of the oriented box. +% +% Example +% % open and display input image +% img = Image.read('circles.png') ; +% figure; show(img); hold on; +% % identifies oriented box around the main region +% obox = regionOrientedBox(img > 0); +% drawOrientedBox(obox, 'g'); +% % crop the content of the oriented box +% res = cropOrientedBox(img, obox); +% figure; show(res) +% +% See also +% regionOrientedBox, crop +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2022-06-24, using Matlab 9.12.0.1884302 (R2022a) +% Copyright 2022 INRAE. + +% retrieve oriented box parameters +boxCenter = obox(1:2); +boxSize = obox(3:4); +boxAngle = obox(5); + +% create the transform matrix that maps from box coords to global coords +transfo = createTranslation(boxCenter) * createRotation(deg2rad(boxAngle)); + +% sample points within the box (use single pixel spacing) +lx = -floor(boxSize(1)/2):ceil(boxSize(1)/2); +ly = -floor(boxSize(2)/2):ceil(boxSize(2)/2); + +% map into global coordinate space +[y, x] = meshgrid(ly, lx); +[x, y] = transformPoint(x, y, transfo); + +% evaluate within image, keeping type of original image +resData = zeros(size(x), class(obj.Data)); +resData(:) = interp(obj, x, y); + +% create new image +res = Image('data', resData, 'parent', obj); diff --git a/src/@Image/defaultConnectivity.m b/src/@Image/defaultConnectivity.m new file mode 100644 index 0000000..9638962 --- /dev/null +++ b/src/@Image/defaultConnectivity.m @@ -0,0 +1,28 @@ +function conn = defaultConnectivity(obj) +% Choose the default foreground connectivity for the image. +% +% CONN = defaultConnectivity(IMG) +% Returns the default connectivity for the input image IMG. +% Returns: +% * CONN = 4 for 2D images, +% * CONN = 6 for 3D images +% +% Example +% defaultConnectivity +% +% See also +% killBorders, fillHoles, reconstruction, watershed +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-04-07, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2021 INRAE. + +if ndims(obj) == 2 %#ok + conn = 4; +elseif ndims(obj) == 3 + conn = 6; +end diff --git a/src/@Image/directionWeights3d13.m b/src/@Image/directionWeights3d13.m new file mode 100644 index 0000000..050a903 --- /dev/null +++ b/src/@Image/directionWeights3d13.m @@ -0,0 +1,547 @@ +function res = directionWeights3d13(varargin) +% Direction weights for 13 directions in 3D. +% +% C = computeDirectionWeights3d13 +% Returns an array of 13-by-1 values, corresponding to directions: +% C(1) = [+1 0 0] +% C(2) = [ 0 +1 0] +% C(3) = [ 0 0 +1] +% C(4) = [+1 +1 0] +% C(5) = [-1 +1 0] +% C(6) = [+1 0 +1] +% C(7) = [-1 0 +1] +% C(8) = [ 0 +1 +1] +% C(9) = [ 0 -1 +1] +% C(10) = [+1 +1 +1] +% C(11) = [-1 +1 +1] +% C(12) = [+1 -1 +1] +% C(13) = [-1 -1 +1] +% The sum of the weights in C equals 1. +% Some values are equal whatever the resolution: +% C(4)==C(5); +% C(6)==C(7); +% C(8)==C(9); +% C(10)==C(11)==C(12)==C(13); +% +% C = computeDirectionWeights3d13(DELTA) +% With DELTA = [DX DY DZ], specifies the resolution of the grid. +% +% Example +% c = computeDirectionWeights3d13; +% sum(c) +% ans = +% 1.0000 +% +% c = computeDirectionWeights3d13([2.5 2.5 7.5]); +% sum(c) +% ans = +% 1.0000 +% +% +% See also +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2010-10-18, using Matlab 7.9.0.529 (R2009b) +% Copyright 2010 INRA - Cepia Software Platform. + + +%% Initializations + +% grid resolution +delta = [1 1 1]; +if ~isempty(varargin) + delta = varargin{1}; +end + +% If resolution is [1 1 1], return the pre-computed set of weights +if all(delta == [1 1 1]) + area1 = 0.04577789120476 * 2; + area2 = 0.03698062787608 * 2; + area3 = 0.03519563978232 * 2; + res = [... + area1; area1; area1; ... + area2; area2; area2; area2; area2; area2;... + area3; area3; area3; area3 ]; + return; +end + +% Define points of interest in the 26 discrete directions +% format is pt[Xpos][Ypos][Zpos], with [X], [Y] or [Z] being one of +% 'N' (for negative), 'P' (for Positive) or 'Z' (for Zero) + +% points below the OXY plane +ptPNN = normalizeVector3d([+1 -1 -1].*delta); +ptPZN = normalizeVector3d([+1 0 -1].*delta); +ptNPN = normalizeVector3d([-1 +1 -1].*delta); +ptZPN = normalizeVector3d([ 0 +1 -1].*delta); +ptPPN = normalizeVector3d([+1 +1 -1].*delta); + +% points belonging to the OXY plane +ptPNZ = normalizeVector3d([+1 -1 0].*delta); +ptPZZ = normalizeVector3d([+1 0 0].*delta); +ptNPZ = normalizeVector3d([-1 +1 0].*delta); +ptZPZ = normalizeVector3d([ 0 +1 0].*delta); +ptPPZ = normalizeVector3d([+1 +1 0].*delta); + +% points above the OXY plane +ptNNP = normalizeVector3d([-1 -1 +1].*delta); +ptZNP = normalizeVector3d([ 0 -1 +1].*delta); +ptPNP = normalizeVector3d([+1 -1 +1].*delta); +ptNZP = normalizeVector3d([-1 0 +1].*delta); +ptZZP = normalizeVector3d([ 0 0 +1].*delta); +ptPZP = normalizeVector3d([+1 0 +1].*delta); +ptNPP = normalizeVector3d([-1 +1 +1].*delta); +ptZPP = normalizeVector3d([ 0 +1 +1].*delta); +ptPPP = normalizeVector3d([+1 +1 +1].*delta); + + +%% Spherical cap type 1, direction [1 0 0] + +% Compute area of voronoi cell for a point on the Ox axis, i.e. a point +% in the 6-neighborhood of the center. +refPoint = ptPZZ; + +% neighbours of chosen point, sorted by CCW angle +neighbors = [ptPNN; ptPNZ; ptPNP; ptPZP; ptPPP; ptPPZ; ptPPN; ptPZN]; + +% compute area of spherical polygon +area1 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Spherical cap type 1, direction [0 1 0] + +% Compute area of voronoi cell for a point on the Oy axis, i.e. a point +% in the 6-neighborhood of the center. +refPoint = ptZPZ; + +% neighbours of chosen point, sorted by angle +neighbors = [ptPPZ; ptPPP; ptZPP; ptNPP; ptNPZ; ptNPN; ptZPN; ptPPN]; + +% compute area of spherical polygon +area2 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Spherical cap type 1, direction [0 0 1] + +% Compute area of voronoi cell for a point on the Oz axis, i.e. a point +% in the 6-neighborhood of the center. +refPoint = ptZZP; + +% neighbours of chosen point, sorted by angle +neighbors = [ptPZP; ptPPP; ptZPP; ptNPP; ptNZP; ptNNP; ptZNP; ptPNP]; + +% compute area of spherical polygon +area3 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Spherical cap type 2, direction [1 1 0] + +% Compute area of voronoi cell for a point on the Oxy plane, i.e. a point +% in the 18-neighborhood +refPoint = ptPPZ; + +% neighbours of chosen point, sorted by angle +neighbors = [ptPZZ; ptPPP; ptZPZ; ptPPN]; + +% compute area of spherical polygon +area4 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Spherical cap type 2, direction [1 0 1] + +% Compute area of voronoi cell for a point on the Oxz plane, i.e. a point +% in the 18-neighborhood +refPoint = ptPZP; +% neighbours of chosen point, sorted by angle +neighbors = [ptPZZ; ptPPP; ptZZP; ptPNP]; + +% compute area of spherical polygon +area5 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Spherical cap type 2, direction [0 1 1] + +% Compute area of voronoi cell for a point on the Oxy plane, i.e. a point +% in the 18-neighborhood +refPoint = ptZPP; +% neighbours of chosen point, sorted by angle +neighbors = [ptZPZ; ptNPP; ptZZP; ptPPP]; + +% compute area of spherical polygon +area6 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Spherical cap type 3 (all cubic diagonals) + +% Compute area of voronoi cell for a point on the Oxyz diagonal, i.e. a +% point in the 26 neighborhood only +refPoint = ptPPP; +% neighbours of chosen point, sorted by angle +neighbors = [ptPZP; ptZZP; ptZPP; ptZPZ; ptPPZ; ptPZZ]; + +% compute area of spherical polygon +area7 = sphericalVoronoiDomainArea(refPoint, neighbors); + + +%% Concatenate results + +% return computed areas, normalized by the area of the unit sphere surface +res = [... + area1 area2 area3 ... + area4 area4 area5 area5 area6 area6... + area7 area7 area7 area7... + ] / (2 * pi); + +function area = sphericalVoronoiDomainArea(refPoint, neighbors) +% Compute area of a spherical voronoi domain +% +% AREA = sphericalVoronoiDomainArea(GERM, NEIGHBORS) +% GERM is a 1-by-3 row vector representing cartesian coordinates of a +% point on the sphere (in X, Y Z order) +% NEIGHBORS is a N-by-3 array representing cartesian coordinates of the +% germ neighbors. It is expected that NEIGHBORS contains only neighbors +% that effectively contribute to the voronoi domain. +% + +% reference sphere +sphere = [0 0 0 1]; + +% number of neigbors, and number of sides of the domain +nbSides = size(neighbors, 1); + +% compute planes containing separating circles +planes = zeros(nbSides, 9); +for i = 1:nbSides + planes(i,1:9) = normalizePlane(medianPlane(refPoint, neighbors(i,:))); +end + +% allocate memory +lines = zeros(nbSides, 6); +intersects = zeros(2*nbSides, 3); + +% compute circle-circle intersections +for i=1:nbSides + lines(i,1:6) = intersectPlanes(planes(i,:), ... + planes(mod(i,nbSides)+1,:)); + intersects(2*i-1:2*i,1:3) = intersectLineSphere(lines(i,:), sphere); +end + +% keep only points in the same direction than refPoint +ind = dot(intersects, repmat(refPoint, [2*nbSides 1]), 2)>0; +intersects = intersects(ind,:); +nbSides = size(intersects, 1); + +% compute spherical area of each triangle [center pt[i+1]%4 pt[i] ] +angles = zeros(nbSides, 1); +for i=1:nbSides + pt1 = intersects(i, :); + pt2 = intersects(mod(i , nbSides)+1, :); + pt3 = intersects(mod(i+1, nbSides)+1, :); + + angles(i) = sphericalAngle(pt1, pt2, pt3); + angles(i) = min(angles(i), 2*pi-angles(i)); +end + +% compute area of spherical polygon +area = sum(angles) - pi*(nbSides-2); + + +function plane = medianPlane(p1, p2) +% Create a plane in the middle of 2 points. + +% unify data dimension +if size(p1, 1) == 1 + p1 = repmat(p1, [size(p2, 1) 1]); +elseif size(p2, 1) == 1 + p2 = repmat(p2, [size(p1, 1) 1]); +elseif size(p1, 1) ~= size(p2, 1) + error('data should have same length, or one data should have length 1'); +end + +% middle point +p0 = (p1 + p2)/2; + +% normal to plane +n = p2 - p1; + +% create plane from point and normal +plane = createPlane(p0, n); + + +function line = intersectPlanes(plane1, plane2, varargin) +% Return intersection line between 2 planes in space. + +tol = 1e-14; +if ~isempty(varargin) + tol = varargin{1}; +end + +% plane normal +n1 = normalizeVector3d(cross(plane1(:,4:6), plane1(:, 7:9), 2)); +n2 = normalizeVector3d(cross(plane2(:,4:6), plane2(:, 7:9), 2)); + +% test if planes are parallel +if abs(cross(n1, n2, 2)) < tol + line = [NaN NaN NaN NaN NaN NaN]; + return; +end + +% Uses Hessian form, ie : N.p = d +% I this case, d can be found as : -N.p0, when N is normalized +d1 = dot(n1, plane1(:,1:3), 2); +d2 = dot(n2, plane2(:,1:3), 2); + +% compute dot products +dot1 = dot(n1, n1, 2); +dot2 = dot(n2, n2, 2); +dot12 = dot(n1, n2, 2); + +% intermediate computations +det = dot1*dot2 - dot12*dot12; +c1 = (d1*dot2 - d2*dot12)./det; +c2 = (d2*dot1 - d1*dot12)./det; + +% compute line origin and direction +p0 = c1*n1 + c2*n2; +dp = cross(n1, n2, 2); + +line = [p0 dp]; + + +function plane = createPlane(p0, n) +% Create a plane in parametrized form +% P = createPlane(P0, N); + +% normal is given by a 3D vector +n = normalizeVector3d(n); + +% ensure same dimension for parameters +if size(p0, 1) == 1 + p0 = repmat(p0, [size(n, 1) 1]); +end +if size(n, 1) == 1 + n = repmat(n, [size(p0, 1) 1]); +end + +% find a vector not colinear to the normal +v0 = repmat([1 0 0], [size(p0, 1) 1]); +inds = vectorNorm3d(cross(n, v0, 2))<1e-14; +v0(inds, :) = repmat([0 1 0], [sum(inds) 1]); + +% create direction vectors +v1 = normalizeVector3d(cross(n, v0, 2)); +v2 = -normalizeVector3d(cross(v1, n, 2)); + +% concatenate result in the array representing the plane +plane = [p0 v1 v2]; + + +function plane2 = normalizePlane(plane1) +% Normalize parametric representation of a plane + +% compute first direction vector +d1 = normalizeVector3d(plane1(:,4:6)); + +% compute second direction vector +n = normalizeVector3d(planeNormal(plane1)); +d2 = -normalizeVector3d(crossProduct3d(d1, n)); + +% compute origin point of the plane +origins = repmat([0 0 0], [size(plane1, 1) 1]); +p0 = projPointOnPlane(origins, [plane1(:,1:3) d1 d2]); + +% create the resulting plane +plane2 = [p0 d1 d2]; + + +function n = planeNormal(plane) +% Compute the normal to a plane + +% plane normal +outSz = size(plane); +outSz(2) = 3; +n = zeros(outSz); +n(:) = crossProduct3d(plane(:,4:6,:), plane(:, 7:9,:)); + +function alpha = sphericalAngle(p1, p2, p3) +% Compute angle between points on the sphere + +% test if points are given as matlab spherical coordinates +if size(p1, 2) == 2 + [x, y, z] = sph2cart(p1(:,1), p1(:,2), ones(size(p1,1), 1)); + p1 = [x y z]; + [x, y, z] = sph2cart(p2(:,1), p2(:,2), ones(size(p2,1), 1)); + p2 = [x y z]; + [x, y, z] = sph2cart(p3(:,1), p3(:,2), ones(size(p3,1), 1)); + p3 = [x y z]; +end + +% normalize points +p1 = normalizeVector3d(p1); +p2 = normalizeVector3d(p2); +p3 = normalizeVector3d(p3); + +% create the plane tangent to the unit sphere and containing central point +plane = createPlane(p2, p2); + +% project the two other points on the plane +pp1 = planePosition(projPointOnPlane(p1, plane), plane); +pp3 = planePosition(projPointOnPlane(p3, plane), plane); + +% compute angle on the tangent plane +pp2 = zeros(max(size(pp1, 1), size(pp3,1)), 2); +alpha = angle3Points(pp1, pp2, pp3); + + +function pos = planePosition(point, plane) +% Compute position of a point on a plane + +% size of input arguments +npl = size(plane, 1); +npt = size(point, 1); + +% check inputs have compatible sizes +if npl ~= npt && npl > 1 && npt > 1 + error('geom3d:planePoint:inputSize', ... + 'plane and point should have same size, or one of them must have 1 row'); +end + +% origin and direction vectors of the plane +p0 = plane(:, 1:3); +d1 = plane(:, 4:6); +d2 = plane(:, 7:9); + +% Compute dot products with direction vectors of the plane +if npl > 1 || npt == 1 + s = dot(bsxfun(@minus, point, p0), d1, 2) ./ vectorNorm3d(d1); + t = dot(bsxfun(@minus, point, p0), d2, 2) ./ vectorNorm3d(d2); +else + % we have npl == 1 and npt > 1 + d1 = d1 / vectorNorm3d(d1); + d2 = d2 / vectorNorm3d(d2); + inds = ones(npt,1); + s = dot(bsxfun(@minus, point, p0), d1(inds, :), 2); + t = dot(bsxfun(@minus, point, p0), d2(inds, :), 2); +end + +% % old version: +% s = dot(point-p0, d1, 2) ./ vectorNorm3d(d1); +% t = dot(point-p0, d2, 2) ./ vectorNorm3d(d2); + +pos = [s t]; + + +function point = projPointOnPlane(point, plane) +% Return the orthogonal projection of a point on a plane + +% Unpack the planes into origins and normals, keeping original shape +plSize = size(plane); +plSize(2) = 3; +[origins, normals] = deal(zeros(plSize)); +origins(:) = plane(:,1:3,:); +normals(:) = crossProduct3d(plane(:,4:6,:), plane(:, 7:9,:)); + +% difference between origins of plane and point +dp = bsxfun(@minus, origins, point); + +% relative position of point on normal's line +t = bsxfun(@rdivide, sum(bsxfun(@times,normals,dp),2), sum(normals.^2,2)); + +% add relative difference to project point back to plane +point = bsxfun(@plus, point, bsxfun(@times, t, normals)); + + +function points = intersectLineSphere(line, sphere, varargin) +%INTERSECTLINESPHERE Return intersection points between a line and a sphere + +% check if user-defined tolerance is given +tol = 1e-14; +if ~isempty(varargin) + tol = varargin{1}; +end + +% difference between centers +dc = bsxfun(@minus, line(:, 1:3), sphere(:, 1:3)); + +% equation coefficients +a = sum(line(:, 4:6) .* line(:, 4:6), 2); +b = 2 * sum(bsxfun(@times, dc, line(:, 4:6)), 2); +c = sum(dc.*dc, 2) - sphere(:,4).*sphere(:,4); + +% solve equation +delta = b.*b - 4*a.*c; + +% initialize empty results +points = NaN * ones(2 * size(delta, 1), 3); + +% process couples with two intersection points +inds = find(delta > tol); +if ~isempty(inds) + % delta positive: find two roots of second order equation + u1 = (-b(inds) -sqrt(delta(inds))) / 2 ./ a(inds); + u2 = (-b(inds) +sqrt(delta(inds))) / 2 ./ a(inds); + + % convert into 3D coordinate + points(inds, :) = line(inds, 1:3) + bsxfun(@times, u1, line(inds, 4:6)); + points(inds+length(delta),:) = line(inds, 1:3) + bsxfun(@times, u2, line(inds, 4:6)); +end + + +function theta = angle3Points(p1, p2, p3) +% Compute oriented angle made by 3 points +theta = lineAngle(createLine(p2, p1), createLine(p2, p3)); + + +function line = createLine(p1, p2) +% Create a line from two points on the line. +line = [p1(:,1), p1(:,2), p2(:,1)-p1(:,1), p2(:,2)-p1(:,2)]; + + +function theta = lineAngle(varargin) +% Computes angle between two straight lines + +if nargin == 1 + % angle of one line with horizontal + line = varargin{1}; + theta = mod(atan2(line(:,4), line(:,3)) + 2*pi, 2*pi); + +elseif nargin == 2 + % angle between two lines + theta1 = lineAngle(varargin{1}); + theta2 = lineAngle(varargin{2}); + theta = mod(bsxfun(@minus, theta2, theta1)+2*pi, 2*pi); +end + + +function c = crossProduct3d(a,b) +% Vector cross product faster than inbuilt MATLAB cross. + +% size of inputs +sizeA = size(a); +sizeB = size(b); + +% Initialise c to the size of a or b, whichever has more dimensions. If +% they have the same dimensions, initialise to the larger of the two +switch sign(numel(sizeA) - numel(sizeB)) + case 1 + c = zeros(sizeA); + case -1 + c = zeros(sizeB); + otherwise + c = zeros(max(sizeA, sizeB)); +end + +c(:) = bsxfun(@times, a(:,[2 3 1],:), b(:,[3 1 2],:)) - ... + bsxfun(@times, b(:,[2 3 1],:), a(:,[3 1 2],:)); + + +function vn = normalizeVector3d(v) +% Normalize a 3D vector to have norm equal to 1 +vn = bsxfun(@rdivide, v, sqrt(sum(v.^2, 2))); + +function n = vectorNorm3d(v) +% Norm of a 3D vector or of set of 3D vectors +n = sqrt(sum(v.*v, 2)); diff --git a/src/@Image/distanceMap.m b/src/@Image/distanceMap.m index 8a8c577..25cb0b6 100644 --- a/src/@Image/distanceMap.m +++ b/src/@Image/distanceMap.m @@ -3,22 +3,28 @@ % % MAP = distanceMap(BIN) % The distance transform is an operator applied to binary images, that -% results in a graylevel image that contains, for each foregournd pixel, +% results in an intensity image that contains, for each foreground pixel, % the distance to the closest background pixel. % +% This function requires binary image as input. For label images that +% contain adjacent regions, the function 'chamferDistanceMap' could be +% more adapted. +% % Example % img = Image.read('circles.png'); % map = distanceMap(img); % show(map) % % See also -% skeleton, geodesicDistanceMap +% skeleton, geodesicDistanceMap, chamferDistanceMap, +% regionInscribedCircle % ------ % Author: David Legland % e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) % Created: 2011-03-27, using Matlab 7.9.0.529 (R2009b) -% Copyright 2011 INRA - Cepia Software Platform. +% Copyright 2021 INRAE. % check type if ~strcmp(obj.Type, 'binary') diff --git a/src/@Image/extendedMaxima.m b/src/@Image/extendedMaxima.m index 4e757d9..8805f78 100644 --- a/src/@Image/extendedMaxima.m +++ b/src/@Image/extendedMaxima.m @@ -29,22 +29,12 @@ error('Requires a Grayscale or intensity image to work'); end -% default value for connectivity -conn = 4; -if obj.Dimension == 3 - conn = 6; -end - -% process input arguments -while ~isempty(varargin) - var = varargin{1}; +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); - if isnumeric(var) && isscalar(var) - % extract connectivity - conn = var; - varargin(1) = []; - continue; - end +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; end data = imextendedmax(obj.Data, dyn, conn); diff --git a/src/@Image/extendedMinima.m b/src/@Image/extendedMinima.m index 30a644a..d8eddef 100644 --- a/src/@Image/extendedMinima.m +++ b/src/@Image/extendedMinima.m @@ -21,23 +21,12 @@ error('Requires a Grayscale or intensity image to work'); end -% default values -conn = 4; +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); -if obj.Dimension == 3 - conn = 6; -end - -% process input arguments -while ~isempty(varargin) - var = varargin{1}; - - if isnumeric(var) && isscalar(var) - % extract connectivity - conn = var; - varargin(1) = []; - continue; - end +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; end data = imextendedmin(obj.Data, dyn, conn); diff --git a/src/@Image/fillHoles.m b/src/@Image/fillHoles.m index 26dc2dd..0f5070b 100644 --- a/src/@Image/fillHoles.m +++ b/src/@Image/fillHoles.m @@ -17,11 +17,8 @@ % Created: 2011-09-11, using Matlab 7.9.0.529 (R2009b) % Copyright 2011 INRA - Cepia Software Platform. -% default connectivity -conn = 4; -if ndims(obj) == 3 - conn = 6; -end +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); % parse input connectivity if ~isempty(varargin) diff --git a/src/@Image/floodFill.m b/src/@Image/floodFill.m index 22b2b4a..5baf523 100644 --- a/src/@Image/floodFill.m +++ b/src/@Image/floodFill.m @@ -5,6 +5,11 @@ % Determines the region of connected pixels with the same value and % containing the position POS, and replaces their value by V. % +% IMG2 = floodFill(IMG, POS, V, CONN) +% Also specifies the connectivity to use. Default connectivity is 4 for +% planar images, and 6 for 3D images. +% +% % Example % % remove the region corresponding to the label at a given position % img = Image.read('coins.png'); @@ -13,7 +18,7 @@ % figure; show(lbl2); colormap jet; % % See also -% reconstruction +% reconstruction, fillHoles, killBorders % % ------ @@ -29,6 +34,14 @@ 'position array size must match image dimension'); end +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); + +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; +end + % create binary image of mask pos = num2cell(pos); mask = obj == obj.Data(pos{:}); @@ -38,7 +51,7 @@ marker.Data(pos{:}) = true; % compute the region composed of connected pixels with same value -rec = reconstruction(marker, mask); +rec = reconstruction(marker, mask, conn); % replace values in result image name = createNewName(obj, '%s-floodFill'); diff --git a/src/@Image/geodesicDistanceMap.m b/src/@Image/geodesicDistanceMap.m index 6e6f779..50ed59c 100644 --- a/src/@Image/geodesicDistanceMap.m +++ b/src/@Image/geodesicDistanceMap.m @@ -65,7 +65,8 @@ % stability is reached. % % See also -% distanceMap, reconstruction +% distanceMap, reconstruction, chamferDistanceMap, +% regionGeodesicDiameter % % ------ @@ -176,7 +177,7 @@ outputType = class(w1); % allocate memory for result -dist = ones(size(mask), outputType); +dist = ones(size(mask.Data), outputType); % init result: either max value, or 0 for marker pixels if isinteger(w1) @@ -184,10 +185,10 @@ else dist(:) = inf; end -dist(marker) = 0; +dist(marker.Data) = 0; % size of image -[D1, D2] = size(mask); +[D1, D2] = size(mask.Data); %% Iterations until no more changes diff --git a/src/@Image/imposeMaxima.m b/src/@Image/imposeMaxima.m index a13a8f4..c3645a9 100644 --- a/src/@Image/imposeMaxima.m +++ b/src/@Image/imposeMaxima.m @@ -21,24 +21,14 @@ error('Requires a Grayscale or intensity image to work'); end -% default values -conn = 4; +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); -if obj.Dimension == 3 - conn = 6; +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; end -% process input arguments -while ~isempty(varargin) - var = varargin{1}; - - if isnumeric(var) && isscalar(var) - % extract connectivity - conn = var; - varargin(1) = []; - continue; - end -end if isa(marker, 'Image') marker = marker.Data; diff --git a/src/@Image/imposeMinima.m b/src/@Image/imposeMinima.m index 530e678..7c577c2 100644 --- a/src/@Image/imposeMinima.m +++ b/src/@Image/imposeMinima.m @@ -22,24 +22,14 @@ error('Requires a Grayscale or intensity image to work'); end -% default values -conn = 4; +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); -if obj.Dimension == 3 - conn = 6; +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; end -% process input arguments -while ~isempty(varargin) - var = varargin{1}; - - if isnumeric(var) && isscalar(var) - % extract connectivity - conn = var; - varargin(1) = []; - continue; - end -end if isa(marker, 'Image') marker = marker.Data; diff --git a/src/@Image/interp.m b/src/@Image/interp.m index ec87fb1..37743a5 100644 --- a/src/@Image/interp.m +++ b/src/@Image/interp.m @@ -3,8 +3,20 @@ % % V = interp(IMG, X, Y) % V = interp(IMG, X, Y, Z) +% Evaluates the value(s) within the image at the specified position(s), +% in pixel coordinates. +% The positions X, Y (and Z for 3D images) must be specified with numeric +% arrays the same size. +% For scalar images, the result V has the same size as the input arrays. +% % V = interp(IMG, POS) +% Speifies the positions as a N-by-2 or N-by-3 array of coordinates. The +% result V is a N-by-1 numeric array. +% % V = interp(..., 'method') +% Specifies the interpolation method to use. Valid options are the ones +% available for the interp2 and interp3 functions. +% Default is 'linear'. % % Example % interp @@ -15,7 +27,7 @@ % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2011-12-14, using Matlab 7.9.0.529 (R2009b) % Copyright 2011 INRA - Cepia Software Platform. @@ -54,7 +66,7 @@ y = yData(obj); if nc == 1 - val = interp2(y, x, double(obj.Data), ... + val(:) = interp2(y, x, double(obj.Data), ... point(:, 2), point(:, 1), method, fillValue); else for i = 1:nc @@ -69,7 +81,7 @@ y = yData(obj); z = zData(obj); if nc == 1 - val = interp3(y, x, z, double(obj.Data), ... + val(:) = interp3(y, x, z, double(obj.Data), ... point(:, 2), point(:, 1), point(:, 3), method, fillValue); else for i = 1:nc diff --git a/src/@Image/killBorders.m b/src/@Image/killBorders.m index 31f12b7..56fa144 100644 --- a/src/@Image/killBorders.m +++ b/src/@Image/killBorders.m @@ -23,10 +23,7 @@ % Copyright 2011 INRA - Cepia Software Platform. % choose default connectivity depending on dimension -conn = 4; -if ndims(obj) == 3 - conn = 6; -end +conn = defaultConnectivity(obj); % case of connectivity specified by user if ~isempty(varargin) diff --git a/src/@Image/largestRegion.m b/src/@Image/largestRegion.m index 5604bd0..828ce37 100644 --- a/src/@Image/largestRegion.m +++ b/src/@Image/largestRegion.m @@ -6,14 +6,14 @@ % image corresponding to obj label. Can be used to select automatically % the most proeminent region in a segmentation or labelling result. % -% [REG IND] = largestRegion(LBL) +% [REG, IND] = largestRegion(LBL) % Also returns the index of the largest region. % % REG = largestRegion(BIN) % REG = largestRegion(BIN, CONN) -% Finds the largest connected region in the binary image IMG. A labelling -% of the image is performed prior to the identification of the largest -% label. The connectivity can be specified. +% Finds the largest connected region in the binary image IMG. A connected +% component labelling of the image is performed prior to the +% identification of the largest label. The connectivity can be specified. % % Example % % Find the binary image corresponding to the largest label @@ -31,7 +31,7 @@ % 0 0 0 0 0 0 % 0 0 0 0 0 0 % -% % Keep largest region in a binary image +% % Keep the largest region in a binary image % BW = Image.read('text.png'); % BW2 = largestRegion(BW, 4); % figure; show(overlay(BW, BW2)); @@ -43,12 +43,13 @@ % show(overlay(img, bin2)); % % See also -% regionprops, killBorders, areaOpening +% regionprops, killBorders, areaOpening, componentLabeling, +% regionElementCounts % % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2012-07-27, using Matlab 7.9.0.529 (R2009b) % Copyright 2012 INRA - Cepia Software Platform. @@ -58,11 +59,10 @@ elseif isBinaryImage(obj) % if image is binary compute labeling - % first determines connectivity to use - conn = 4; - if obj.Dimension == 3 - conn = 6; - end + % choose default connectivity depending on dimension + conn = defaultConnectivity(obj); + + % case of connectivity specified by user if ~isempty(varargin) conn = varargin{1}; end diff --git a/src/@Image/medianFilter.m b/src/@Image/medianFilter.m index fc21f67..9868906 100644 --- a/src/@Image/medianFilter.m +++ b/src/@Image/medianFilter.m @@ -1,14 +1,18 @@ function res = medianFilter(obj, se, varargin) % Compute median value in the neighboorhood of each pixel. % +% RES = medianFilter(IMG2D, [M N]) +% RES = medianFilter(IMG3D, [M N P]) +% Applies median filtering to the input image, by computing the median +% value in the square or cubic neighborhood of each image element. +% % RES = medianFilter(IMG, SE) -% Compute the mean filter of image IMG, using structuring element SE. -% The goal of obj function is to provide the same interface as for -% other image filters (imopen, imerode ...), and to allow the use of -% mean filter with user-defined structuring element. +% Compute the median filter of image IMG, using structuring element SE. +% The goal of this function is to provide the same interface as for +% other image filters (opening, erosion...), and to allow the use of +% median filter with user-defined structuring element. % This function can be used for directional filtering. % -% % RES = medianFilter(IMG, SE, PADOPT) % also specify padding option. PADOPT can be one of: % 'zeros' @@ -17,21 +21,28 @@ % see ordfilt2 for details. Default is 'symmetric' (contrary to the % default for ordfilt2). % +% Implementation notes +% When neighborhood is given as a 1-by-2 or 1-by-3 array, the methods is +% a wrapper for the medfilt2 or medfilt3 function. Otherwise, the +% ordfilt2 function ise used. +% +% Example +% % apply median filtering on rice image +% img = Image.read('rice.png'); +% imgf = medianFilter(img, [3 3]); +% figure; show(imgf); +% % See also: % meanFilter, ordfilt2, median % % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2011-08-05, using Matlab 7.9.0.529 (R2009b) % Copyright 2011 INRA - Cepia Software Platform. -if obj.Dimension > 2 - error('Median filter implemented only for planar images'); -end - % transform STREL object into single array if isa(se, 'strel') se = getnhood(se); @@ -43,13 +54,26 @@ padopt = varargin{1}; end -% rotate structuring element -se = permute(se, [2 1 3:5]); - -% perform filtering -order = ceil(sum(se(:)) / 2); -data = ordfilt2(obj.Data, order, se, padopt); +if isnumeric(se) && all(size(se) == [1 2]) && obj.Dimension == 2 + % if input corresponds to filter size, use medfilt2 + data = medfilt2(obj.Data, se([2 1])); + +elseif isnumeric(se) && all(size(se) == [1 3]) && obj.Dimension == 3 + % process the 3D case, only for cubic neighborhoods + data = medfilt3(obj.Data, se([2 1 3])); + +else + % otherwise, use the ordfilt2 function by choosing the order according + % to SE size. + + % rotate structuring element + se = permute(se, [2 1 3:5]); + + % perform filtering + order = ceil(sum(se(:)) / 2); + data = ordfilt2(obj.Data, order, se, padopt); +end % create result image name = createNewName(obj, '%s-medianFilt'); -res = Image('Data', data, 'Parent', obj, 'Name', name); \ No newline at end of file +res = Image('Data', data, 'Parent', obj, 'Name', name); diff --git a/src/@Image/read.m b/src/@Image/read.m index f668dcc..e504f28 100644 --- a/src/@Image/read.m +++ b/src/@Image/read.m @@ -39,8 +39,30 @@ % Created: 2010-07-13, using Matlab 7.9.0.529 (R2009b) % Copyright 2010 INRAE - Cepia Software Platform. -% extract filename's extension -[path, name, ext] = fileparts(fileName); %#ok +% parse fileName components +[filePath, baseName, ext] = fileparts(fileName); + +% if file does not exist, try to add the path to the list of sample files +if exist(fileName, 'file') == 0 && isempty(filePath) + % retrieve the path to sample files + [filePath, ~] = fileparts(mfilename('fullpath')); + filePath = fullfile(filePath, 'sampleFiles'); + + % create newfile path + fileName = fullfile(filePath, [baseName ext]); + + % if file still does not exist, try to add default known extension + if exist(fileName, 'file') == 0 && isempty(ext) + knownExtensions = {'.tif', '.png'}; + for iExt = 1:length(knownExtensions) + ext = knownExtensions{iExt}; + fileName = fullfile(filePath, [baseName ext]); + if exist(fileName, 'file') > 0 + break; + end + end + end +end % try to deduce format from extension. % First use reader provided by Matlab then use readers in private directory. @@ -108,7 +130,7 @@ end % populate additional meta-data -img.Name = name; +img.Name = baseName; img.FilePath = fileName; end @@ -118,8 +140,13 @@ % % Can manage 3D images as well. +% disable some warnings specific to TIFF format +warning('off', 'imageio:tifftagsread:zeroComponentCount'); % check if the file contains a 3D image infoList = imfinfo(fileName); +% re-enable warnings +warning('on', 'imageio:tifftagsread:zeroComponentCount'); + info1 = infoList(1); read3d = false; if length(infoList) > 1 @@ -177,14 +204,15 @@ % default values nImages = size(img, 3); -nSlices = size(img, 3); -nChannels = size(img, 4); -nFrames = size(img, 5); +nSlices = 1; +nChannels = 1; +nFrames = 1; hyperstack = 'false'; %#ok spacing = img.Spacing; origin = img.Origin; unitName = img.UnitName; timeStep = img.TimeStep; +timeUnit = img.TimeUnit; % parse tokens in the "ImageDescription' Tag. tokens = regexp(desc, '\n', 'split'); @@ -214,6 +242,9 @@ origin = [0 0 0]; case 'finterval' timeStep = str2double(value); + case 'fps' + timeStep = 1 / str2double(value); + timeUnit = 's'; case {'min', 'max'} % nothing to do. otherwise @@ -239,6 +270,7 @@ img.Origin = origin; img.UnitName = unitName; img.TimeStep = timeStep; +img.TimeUnit = timeUnit; end diff --git a/src/@Image/reconstruction.m b/src/@Image/reconstruction.m index aefc6b2..922bd08 100644 --- a/src/@Image/reconstruction.m +++ b/src/@Image/reconstruction.m @@ -25,15 +25,28 @@ % ------ % Author: David Legland -% e-mail: david.legland@inra.fr +% e-mail: david.legland@inrae.fr % Created: 2011-08-01, using Matlab 7.9.0.529 (R2009b) % Copyright 2011 INRA - Cepia Software Platform. % HISTORY + +% Parse input arguments [marker, mask, parent] = parseInputCouple(marker, mask); -data = imreconstruct(marker, mask, varargin{:}); + +% choose default connectivity depending on dimension +conn = defaultConnectivity(parent); + +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; +end + + +% process reconstruction +data = imreconstruct(marker, mask, conn); % create result image -name = createNewName(parent, '%s-minima'); +name = createNewName(parent, '%s-rec'); res = Image('Data', data, 'Parent', parent, 'Type', parent.Type, 'Name', name); diff --git a/src/@Image/regionArea.m b/src/@Image/regionArea.m new file mode 100644 index 0000000..a63382d --- /dev/null +++ b/src/@Image/regionArea.m @@ -0,0 +1,56 @@ +function [area, labels] = regionArea(obj, varargin) +% Area of regions within a 2D binary or label image. +% +% A = regionArea(IMG); +% Compute the area of the regions in the image IMG. IMG can be either a +% binary image, or a label image. If IMG is binary, a single area is +% returned. In the case of a label image, the area of each region is +% returned in a column vector with as many elements as the number of +% regions. +% +% A = regionArea(..., LABELS); +% In the case of a label image, specifies the labels for which the area +% need to be computed. +% +% Example +% img = Image.read('rice.png'); +% img2 = img - opening(img, ones(30, 30)); +% lbl = componentLabeling(img2 > 50, 4); +% areas = regionArea(lbl); +% +% See Also +% regionprops, regionPerimeter, regionEulerNumber, regionElementCount +% regionVolume + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check dimensionality +nd = ndims(obj); +if nd ~= 2 + error('Requires a 2-dimensional image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end + +% count the number of elements, and multiply by pixel volume +pixelCounts = regionElementCount(obj, labels); +area = pixelCounts * prod(obj.Spacing(1:2)); diff --git a/src/@Image/regionBoundingBox.m b/src/@Image/regionBoundingBox.m new file mode 100644 index 0000000..ff22aab --- /dev/null +++ b/src/@Image/regionBoundingBox.m @@ -0,0 +1,103 @@ +function [boxes, labels] = regionBoundingBox(obj, varargin) +% Bounding box of regions within a 2D or 3D binary or label image. +% +% BOX = regionBoundingBox(IMG) +% Compute the bounding boxes of the regions within the label image IMG. +% If the image is binary, a single box corresponding to the foreground +% (i.e. the pixels with value 1) is computed. +% +% The result is a N-by-4 array BOX = [XMIN XMAX YMIN YMAX], containing +% coordinates of the box extent. +% +% The same result could be obtained with the regionprops function. The +% advantage of using regionBoxes is that equivalent boxes can be +% obtained in one call. +% +% BOX = regionBoundingBox(IMG3D) +% If input image is a 3D array, the result is a N-by-6 array, containing +% the maximal coordinates in the X, Y and Z directions: +% BOX = [XMIN XMAX YMIN YMAX ZMIN ZMAX]. +% +% [BOX, LABELS] = regionBoundingBox(...) +% Also returns the labels of the regions for which a bounding box was +% computed. LABELS is a N-by-1 array with as many rows as BOX. +% +% [...] = regionBoxes(IMG, LABELS) +% Specifies the labels of the regions whose bounding box need to be +% computed. +% +% +% Example +% % Compute bounding box of coins regions +% img = Image.read('coins.png'); % read image +% bin = opening(img > 80, ones([3 3])); % binarize +% lbl = componentLabeling(bin); % compute labels +% figure; show(img); % display image +% boxes = regionBoundingBox(lbl); % compute bounding boxes +% hold on; drawBox(boxes, 'b'); % display boxes +% +% See also +% drawBox, regionCentroid +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-02-02, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2021 INRAE. + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && isnumeric(varargin{1}) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end + +% switch processing depending on dimension +nd = ndims(obj); +if nd == 2 + %% Process planar case + props = regionprops(obj.Data, 'BoundingBox'); + props = props(labels); + bb = reshape([props.BoundingBox], [4 length(props)])'; + + % convert to (x,y) indexing convention + boxes = [bb(:, 2) bb(:, 2)+bb(:, 4) bb(:, 1) bb(:, 1)+bb(:, 3)]; + + % spatial calibration + if isCalibrated(obj) + spacing = obj.Spacing([1 1 2 2]); + origin = obj.Origin([1 1 2 2]); + boxes = bsxfun(@plus, bsxfun(@times, (boxes - 1), spacing), origin); + end + +elseif nd == 3 + %% Process 3D case + props = regionprops3(obj.Data, 'BoundingBox'); + props = props(labels); + bb = reshape([props.BoundingBox], [6 size(props, 1)])'; + bb = bb(labels, :); + + % convert to (x,y,z) indexing convention + boxes = [bb(:, 2) bb(:, 2)+bb(:, 5) bb(:, 1) bb(:, 1)+bb(:, 4) bb(:, 3) bb(:, 3)+bb(:, 6)]; + + % spatial calibration + if isCalibrated(obj) + spacing = obj.Spacing([1 1 2 2 3 3]); + origin = obj.Origin([1 1 2 2 3 3]); + boxes = bsxfun(@plus, bsxfun(@times, (boxes - 1), spacing), origin); + end + +else + error('Image dimension must be 2 or 3'); +end diff --git a/src/@Image/regionBoxes.m b/src/@Image/regionBoxes.m index 2f6900a..5849a9b 100644 --- a/src/@Image/regionBoxes.m +++ b/src/@Image/regionBoxes.m @@ -1,6 +1,8 @@ function [boxes, labels] = regionBoxes(obj, varargin) % Bounding box of regions within a 2D or 3D binary or label image. % +% Note: deprecated, use 'regionBoundingBox' instead +% % BOX = regionBoxes(IMG) % Compute the bounding boxes of the regions within the label image IMG. % If the image is binary, a single box corresponding to the foreground @@ -47,6 +49,8 @@ % Created: 2021-02-02, using Matlab 9.8.0.1323502 (R2020a) % Copyright 2021 INRAE. +warning('Function "regionBoxes" is deprecated, use "regionBoundingBox" instead'); + % check image type if ~(isLabelImage(obj) || isBinaryImage(obj)) error('Requires a label of binary image'); diff --git a/src/@Image/regionCentroid.m b/src/@Image/regionCentroid.m new file mode 100644 index 0000000..1280eef --- /dev/null +++ b/src/@Image/regionCentroid.m @@ -0,0 +1,91 @@ +function [points, labels] = regionCentroid(obj, varargin) +% Centroid of region(s) in a binary or label image. +% +% C = regionCentroid(I) +% Returns the centroid C of the binary image I. C is a 1-by-2 or 1-by-3 +% row vector, depending on the dimension of the image. +% +% C = regionCentroid(LBL) +% If LBL is a label D-dimensional image, returns an array of N-by-D +% values, corresponding to the centroids of the N regions within the +% image. +% +% [C, LABELS] = regionCentroid(LBL) +% Also returns the labels of the regions that were measured. +% +% Example +% % Compute centroids of coins particles +% img = Image.read('coins.png'); % read image +% bin = opening(img > 80, ones([3 3])); % binarize +% lbl = componentLabeling(bin); % compute labels +% figure; show(img); % display image +% pts = regionCentroid(lbl); % compute centroids +% hold on; plot(pts(:,1), pts(:,2), 'b+'); % display centroids +% +% See also +% analyzeRegions, findRegionLabels, componentLabeling, regionprops +% regionEquivalentEllipse, regionBoundingBox + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2018-07-03, using Matlab 9.4.0.813654 (R2018a) +% Copyright 2018 INRA - Cepia Software Platform. + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + +% allocate memory for result +nd = ndims(obj); +points = zeros(nLabels, nd); + +% switch processing depending on image dimensionality +if nd == 2 + for i = 1:nLabels + % extract points of the current region + [x, y] = find(obj.Data == labels(i)); + + % coordinates of particle regionCentroid + xc = mean(x); + yc = mean(y); + + points(i, :) = [xc yc]; + end + +elseif nd == 3 + dim = size(obj.Data); + for i = 1:nLabels + % extract points of the current region + inds = find(obj.Data == labels(i)); + [x, y, z] = ind2sub(dim, inds); + + % coordinates of particle regionCentroid + xc = mean(x); + yc = mean(y); + zc = mean(z); + + points(i, :) = [xc yc zc]; + end + +else + error('Requires an image of dimension 2 or 3'); +end + +% calibrate result +if isCalibrated(obj) + points = bsxfun(@plus, bsxfun(@times, points-1, obj.Spacing), obj.Origin); +end diff --git a/src/@Image/regionCentroids.m b/src/@Image/regionCentroids.m index a940903..8b05146 100644 --- a/src/@Image/regionCentroids.m +++ b/src/@Image/regionCentroids.m @@ -1,6 +1,8 @@ function [points, labels] = regionCentroids(obj, varargin) % Centroid of region(s) in a binary or label image. % +% Note: deprecated, use 'regionCentroid' instead +% % C = regionCentroids(I) % Returns the centroid C of the binary image I. C is a 1-by-2 or 1-by-3 % row vector, depending on the dimension of the image. @@ -32,6 +34,8 @@ % Created: 2018-07-03, using Matlab 9.4.0.813654 (R2018a) % Copyright 2018 INRA - Cepia Software Platform. +warning('Function "regionCentroids" is deprecated, use "regionCentroid" instead'); + % check image type if ~(isLabelImage(obj) || isBinaryImage(obj)) error('Requires a label of binary image'); diff --git a/src/@Image/regionElementCount.m b/src/@Image/regionElementCount.m new file mode 100644 index 0000000..96a00c0 --- /dev/null +++ b/src/@Image/regionElementCount.m @@ -0,0 +1,54 @@ +function [counts, labels] = regionElementCount(obj, varargin) +% Count the number of pixels/voxels within each region of a label image. +% +% CNT = regionElementCount(LBL) +% For each region on the label image LBL, count the number of elements +% (pixels or voxels) that constitute this region. Return a column vector +% with as many elements as the number of regions. +% +% [CNT, LABELS] = regionElementCount(LBL) +% Also returns the labels of the regions. +% +% Example +% img = Image.read('coins.png'); +% bin = fillHoles(img > 100); +% lbl = componentLabeling(bin); +% regionElementCount(lbl)' +% ans = +% 2563 1899 2598 1840 2693 1906 2648 2725 1935 2796 +% +% See also +% regionCentroids, findRegionLabels, largestRegion +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2020-12-02, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2020 INRAE. + +% check input type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label image as input'); +end + +% determine labels +if isempty(varargin) + labels = unique(obj.Data(:)); + labels(labels==0) = []; +else + labels = varargin{1}; +end + +% rely on regionprops for speed +if size(obj.Data, 3) == 1 + props = regionprops(obj.Data, 'Area'); + counts = [props.Area]'; + counts = counts(labels); +else + props = regionprops3(obj.Data, 'Volume'); + counts = [props.Volume]; + counts = counts(labels); +end + \ No newline at end of file diff --git a/src/@Image/regionElementCounts.m b/src/@Image/regionElementCounts.m index 79683f4..0d99c0b 100644 --- a/src/@Image/regionElementCounts.m +++ b/src/@Image/regionElementCounts.m @@ -1,6 +1,8 @@ function [counts, labels] = regionElementCounts(obj, varargin) % Count the number of pixels/voxels within each region of a label image. % +% Note: deprecated, use 'regionElementCount' instead +% % CNT = regionElementCounts(LBL) % For each region on the label image LBL, count the number of elements % (pixels or voxels) that constitute this region. Return a column vector @@ -18,7 +20,7 @@ % 2563 1899 2598 1840 2693 1906 2648 2725 1935 2796 % % See also -% regionCentroids, findRegionLabels +% regionCentroids, findRegionLabels, largestRegion % % ------ @@ -28,6 +30,8 @@ % Created: 2020-12-02, using Matlab 9.8.0.1323502 (R2020a) % Copyright 2020 INRAE. +warning('Function "regionElementCounts" is deprecated, use "regionElementCount" instead'); + % check input type if ~isLabelImage(obj) error('Requires a label image as input'); diff --git a/src/@Image/regionEquivalentEllipse.m b/src/@Image/regionEquivalentEllipse.m new file mode 100644 index 0000000..8255f1e --- /dev/null +++ b/src/@Image/regionEquivalentEllipse.m @@ -0,0 +1,124 @@ +function [ellipse, labels] = regionEquivalentEllipse(obj, varargin) +% Equivalent ellipse of region(s) in a binary or label image. +% +% ELLI = regionEquivalentEllipse(IMG) +% Computes the ellipse with same second order moments for each region in +% label image IMG. If the case of a binary image, a single ellipse +% corresponding to the foreground (i.e. to the region with pixel value 1) +% will be computed. +% +% The result is a N-by-5 array ELLI = [XC YC A B THETA], containing the +% coordinates of ellipse center, the lengths of major and minor +% semi-axes, and the orientation of the largest axis (in degrees, +% counter-clockwise). +% +% ELLI = regionEquivalentEllipse(..., LABELS) +% Specifies the labels for which the equivalent ellipse needs to be +% computed. The result is a N-by-5 array with as many rows as the number +% of labels. +% +% +% Example +% % Draw a complex particle together with its equivalent ellipse +% img = Image.read('circles.png'); +% show(img); hold on; +% elli = regionEquivalentEllipse(img); +% drawEllipse(elli); % requires the MatGeom toolbox +% +% % Compute and display the equivalent ellipses of several regions +% img = Image.read('rice.png'); +% img2 = img - opening(img, ones(30, 30)); +% lbl = componentLabeling(img2 > 50, 4); +% ellipses = regionEquivalentEllipse(lbl); +% show(img); hold on; +% drawEllipse(ellipses, 'linewidth', 2, 'color', 'g'); +% +% See also +% regionCentroid, regionBoundingBox, regionEquivalentEllipsoid, +% drawEllipse, regionInscribedCircle +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-02-02, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2021 INRAE. + +%% check image type and dimension +if ~(isBinaryImage(obj) || isLabelImage(obj)) + error('Requires a label of binary image'); +end +if ndims(obj) ~= 2 %#ok + error('Requires a 2D image'); +end + + +%% Retrieve spatial calibration + +% extract calibration +spacing = obj.Spacing; +origin = obj.Origin; +calib = isCalibrated(obj); + + +%% Initialisations + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + +% allocate memory for result +ellipse = zeros(nLabels, 5); + + +%% Extract ellipse corresponding to each label + +for i = 1:nLabels + % extract points of the current region + [x, y] = find(obj.Data==labels(i)); + + % transform to physical space if needed + if calib + x = (x-1) * spacing(1) + origin(1); + y = (y-1) * spacing(2) + origin(2); + end + + % compute centroid, used as center of equivalent ellipse + xc = mean(x); + yc = mean(y); + + % recenter points (should be better for numerical accuracy) + x = x - xc; + y = y - yc; + + % number of points + n = length(x); + + % compute second order parameters. 1/12 is the contribution of a single + % pixel, then for regions with only one pixel the resulting ellipse has + % positive radii. + Ixx = sum(x.^2) / n + spacing(1)^2/12; + Iyy = sum(y.^2) / n + spacing(2)^2/12; + Ixy = sum(x.*y) / n; + + % compute semi-axis lengths of ellipse + common = sqrt( (Ixx - Iyy)^2 + 4 * Ixy^2); + ra = sqrt(2) * sqrt(Ixx + Iyy + common); + rb = sqrt(2) * sqrt(Ixx + Iyy - common); + + % compute ellipse angle and convert into degrees + theta = atan2(2 * Ixy, Ixx - Iyy) / 2; + theta = theta * 180 / pi; + + % create the resulting equivalent ellipse + ellipse(i,:) = [xc yc ra rb theta]; +end diff --git a/src/@Image/regionEquivalentEllipses.m b/src/@Image/regionEquivalentEllipses.m index 8240f53..27655e5 100644 --- a/src/@Image/regionEquivalentEllipses.m +++ b/src/@Image/regionEquivalentEllipses.m @@ -1,6 +1,8 @@ function [ellipse, labels] = regionEquivalentEllipses(obj, varargin) % Equivalent ellipse of region(s) in a binary or label image. % +% Note: deprecated, use 'regionEquivalentEllipse' instead +% % ELLI = regionEquivalentEllipses(IMG) % Computes the ellipse with same second order moments for each region in % label image IMG. If the case of a binary image, a single ellipse @@ -34,7 +36,7 @@ % drawEllipse(ellipses, 'linewidth', 2, 'color', 'g'); % % See also -% regionCentroids, regionBoxes +% regionCentroids, regionBoxes, regionOrientedBox % % ------ @@ -44,6 +46,7 @@ % Created: 2021-02-02, using Matlab 9.8.0.1323502 (R2020a) % Copyright 2021 INRAE. +warning('Function "regionEquivalentEllipses" is deprecated, use "regionEquivalentEllipse" instead'); %% extract spatial calibration diff --git a/src/@Image/regionEquivalentEllipsoid.m b/src/@Image/regionEquivalentEllipsoid.m new file mode 100644 index 0000000..31e7933 --- /dev/null +++ b/src/@Image/regionEquivalentEllipsoid.m @@ -0,0 +1,190 @@ +function [ellipsoid, labels] = regionEquivalentEllipsoid(obj, varargin) +% Equivalent ellipsoid of region(s) in a 3D binary or label image. +% +% ELLI = regionEquivalentEllipsoid(IMG) +% Where IMG is a binary image of a single region. +% ELLI = [XC YC ZC A B C PHI THETA PSI] is an ellipsoid defined by its +% center [XC YC ZC], 3 radiusses A, B anc C, and a 3D orientation angle +% given by (PHI, THETA, PSI). +% +% ELLI = regionEquivalentEllipsoid(LBL) +% Compute the ellipsoid with same second order moments for each region in +% label image LBL. If the case of a binary image, a single ellipsoid +% corresponding to the foreground (i.e. to the region with voxel value 1) +% will be computed. +% The result ELLI is NL-by-9 array, with NL being the number of unique +% labels in the input image. +% +% ELLI = regionEquivalentEllipsoid(..., LABELS) +% Specify the labels for which the ellipsoid needs to be computed. The +% result is a N-by-9 array with as many rows as the number of labels. +% +% Example +% % Generate an ellipsoid image and computes the equivalent ellipsoid +% % (one expects to obtain nearly same results) +% elli = [50 50 50 50 30 10 40 30 20]; +% img = Image(discreteEllipsoid(1:100, 1:100, 1:100, elli)); +% elli2 = regionEquivalentEllipsoid(img) +% elli2 = +% 50.00 50.00 50.00 50.0072 30.0032 10.0072 40.0375 29.9994 20.0182 +% +% % Draw equivalent ellipsoid of human head image +% % (requires image processing toolbox, and slicer program for display) +% img = Image.read('brainMRI.hdr'); +% img.Spacing = [1 1 2.5]; % fix spacing for display +% bin = closing(img > 0, ones([3 3 3])); +% figure; showOrthoSlices(img, [60 80 13]); +% axis equal; view(3); +% elli = regionEquivalentEllipsoid(bin); +% drawEllipsoid(elli, 'FaceAlpha', 0.5) % requires MatGeom library +% +% See also +% regionEquivalentEllipse, regionBoundingBox, regionCentroid +% drawEllipsoid, regionprops3 +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-03, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% check image type and dimension +if ~(isBinaryImage(obj) || isLabelImage(obj)) + error('Requires a label of binary image'); +end +if ndims(obj) ~= 3 + error('Requires a 3D image'); +end + + +%% Retrieve spatial calibration + +% extract calibration +spacing = obj.Spacing; +origin = obj.Origin; +dim = size(obj); + + +%% Initialisations + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end + +% allocate memory for result +nLabels = length(labels); +ellipsoid = zeros(nLabels, 9); + +for i = 1:nLabels + % extract position of voxels for the current region + inds = find(obj.Data == labels(i)); + if isempty(inds) + continue; + end + [x, y, z] = ind2sub(dim, inds); + + % compute approximate location of ellipsoid center + xc = mean(x); + yc = mean(y); + zc = mean(z); + + % compute center (in pixel coordinates) + center = [xc yc zc]; + + % recenter points (should be better for numerical accuracy) + x = (x - xc) * spacing(1); + y = (y - yc) * spacing(2); + z = (z - zc) * spacing(3); + + points = [x y z]; + + % compute the covariance matrix + covPts = cov(points, 1) + diag(spacing.^2 / 12); + + % perform a principal component analysis with 3 variables, + % to extract equivalent axes + [U, S] = svd(covPts); + + % extract length of each semi axis + radii = sqrt(5) * sqrt(diag(S))'; + + % sort axes from greater to lower + [radii, ind] = sort(radii, 'descend'); + + % format U to ensure first axis points to positive x direction + U = U(ind, :); + if U(1,1) < 0 + U = -U; + % keep matrix determinant positive + U(:,3) = -U(:,3); + end + + % convert axes rotation matrix to Euler angles + angles = rotation3dToEulerAngles(U); + + % concatenate result to form an ellipsoid object + center = (center - 1) .* spacing + origin; + ellipsoid(i, :) = [center radii angles]; +end + + +function varargout = rotation3dToEulerAngles(mat) +% Extract Euler angles from a rotation matrix. +% +% [PHI, THETA, PSI] = rotation3dToEulerAngles(MAT) +% Computes Euler angles PHI, THETA and PSI (in degrees) from a 3D 4-by-4 +% or 3-by-3 rotation matrix. +% +% ANGLES = rotation3dToEulerAngles(MAT) +% Concatenates results in a single 1-by-3 row vector. This format is used +% for representing some 3D shapes like ellipsoids. +% +% Example +% rotation3dToEulerAngles +% +% References +% Code from Graphics Gems IV on euler angles +% http://tog.acm.org/resources/GraphicsGems/gemsiv/euler_angle/EulerAngles.c +% Modified using explanations in: +% http://www.gregslabaugh.name/publications/euler.pdf +% +% See also +% MatGeom library + +% conversion from radians to degrees +k = 180 / pi; + +% extract |cos(theta)| +cy = hypot(mat(1, 1), mat(2, 1)); + +% avoid dividing by 0 +if cy > 16*eps + % normal case: theta <> 0 + psi = k * atan2( mat(3, 2), mat(3, 3)); + theta = k * atan2(-mat(3, 1), cy); + phi = k * atan2( mat(2, 1), mat(1, 1)); +else + % theta close to 0 + psi = k * atan2(-mat(2, 3), mat(2, 2)); + theta = k * atan2(-mat(3, 1), cy); + phi = 0; +end + +% format output arguments +if nargout <= 1 + % one array + varargout{1} = [phi theta psi]; +else + % three separate arrays + varargout = {phi, theta, psi}; +end diff --git a/src/@Image/regionEulerNumber.m b/src/@Image/regionEulerNumber.m new file mode 100644 index 0000000..308b4d6 --- /dev/null +++ b/src/@Image/regionEulerNumber.m @@ -0,0 +1,351 @@ +function [chi, labels] = regionEulerNumber(obj, varargin) +% Euler number of regions within a binary or label image. +% +% The function computes the Euler number, or Euler-Poincare +% characteristic, of a binary 2D image. The result corresponds to the +% number of connected components, minus the number of holes in the image. +% +% CHI = regionEulerNumber(IMG); +% Return the Euler-Poincaré Characteristic of the binary structure within +% the image IMG. +% +% CHI = regionEulerNumber(IMG, CONN); +% Specifies the connectivity used. Currently 4 and 8 connectivities are +% supported. +% +% Example +% img = Image.read('coins.png'); +% bin = closing(img>80, ones(3,3)); +% regionEulerNumber(bin) +% ans = +% 10 +% +% See Also: +% regionArea, regionPerimeter, regionSurfaceArea, regionprops +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Parse input arguments + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check image dimension +nd = ndims(obj); +if nd == 2 + conn = 4; +elseif nd == 3 + conn = 6; +else + error('First argument should be a 2D or a 3D image'); +end + +% default options +labels = []; + + +% parse input arguments +while ~isempty(varargin) + var1 = varargin{1}; + varargin(1) = []; + + if isscalar(var1) + % connectivity + conn = var1; + elseif size(var1, 2) == 1 + % the labels to compute + labels = var1; + else + error('Unable to interpret input argument'); + end +end + + +%% Process label image + +if isBinaryImage(obj) + % in case of binary image, compute only one label + if nd == 2 + chi = eulerNumberBinary2d(obj.Data, conn); + else + chi = eulerNumberBinary3d(obj.Data, conn); + end + labels = 1; + +elseif isLabelImage(obj) + % in case of a label image, return a vector with a set of results. + + % extract labels if necessary (considers 0 as background) + if isempty(labels) + labels = findRegionLabels(obj); + end + + % allocate result array + nLabels = length(labels); + chi = zeros(nLabels, 1); + + % compute bounding box of each region + bounds = regionMinMaxIndices(obj, labels); + + % compute Euler number of each label considered as binary image + if nd == 2 + for i = 1:nLabels + label = labels(i); + + % convert bounding box to image extent, in x and y directions + bx = bounds(i, [1 2]); + by = bounds(i, [3 4]); + + bin = obj.Data(bx(1):bx(2), by(1):by(2)) == label; + + chi(i) = eulerNumberBinary2d(bin, conn); + end + else + for i = 1:nLabels + label = labels(i); + + % convert bounding box to image extent, in x and y directions + bx = bounds(i, [1 2]); + by = bounds(i, [3 4]); + bz = bounds(i, [5 6]); + + bin = obj.Data(bx(1):bx(2), by(1):by(2), bz(1):bz(2)) == label; + chi(i) = eulerNumberBinary3d(bin, conn); + end + end +else + error('Wrong type of image'); +end + + +%% Process 2D binary image +function chi = eulerNumberBinary2d(img, conn) +% Compute euler number on a 2D binary image. +% +% Axis order of array follow physical order: X, Y. +% + +% size of image in each direction +N1 = size(img, 1); +N2 = size(img, 2); + +% compute number of nodes, number of edges (H and V) and number of faces. +% principle is erosion with simple structural elements (line, square) +% but it is replaced here by simple boolean operation, which is faster + +% count vertices +n = sum(img(:)); + +% count horizontal and vertical edges +n1 = sum(sum(img(1:N1-1,:) & img(2:N1,:))); +n2 = sum(sum(img(:,1:N2-1) & img(:,2:N2))); + +% count square faces +n1234 = sum(sum(... + img(1:N1-1,1:N2-1) & img(1:N1-1,2:N2) & ... + img(2:N1,1:N2-1) & img(2:N1,2:N2) )); + +if conn == 4 + % compute euler characteristics from graph counts + chi = n - n1 - n2 + n1234; + return; + +elseif conn == 8 + % For 8-connectivity, need also to count diagonal edges + n3 = sum(sum(img(1:N1-1,1:N2-1) & img(2:N1,2:N2))); + n4 = sum(sum(img(1:N1-1,2:N2) & img(2:N1,1:N2-1))); + + % and triangular faces + n123 = sum(sum(img(1:N1-1,1:N2-1) & img(1:N1-1,2:N2) & img(2:N1,1:N2-1) )); + n124 = sum(sum(img(1:N1-1,1:N2-1) & img(1:N1-1,2:N2) & img(2:N1,2:N2) )); + n134 = sum(sum(img(1:N1-1,1:N2-1) & img(2:N1,1:N2-1) & img(2:N1,2:N2) )); + n234 = sum(sum(img(1:N1-1,2:N2) & img(2:N1,1:N2-1) & img(2:N1,2:N2) )); + + % compute euler characteristics from graph counts + % chi = Nvertices - Nedges + Ntriangles + Nsquares + chi = n - (n1+n2+n3+n4) + (n123+n124+n134+n234) - n1234; + +else + error('regionEulerNumber: uknown connectivity option'); +end + +%% Process 3D binary image +function chi = eulerNumberBinary3d(img, conn) +% Compute euler number on a 3D binary image. + +% size of image in each direction +dim = size(img); +N1 = dim(1); +N2 = dim(2); +N3 = dim(3); + +% compute number of nodes, number of edges in each direction, number of +% faces in each plane, and number of cells. +% principle is erosion with simple structural elements (line, square) +% but it is replaced here by simple boolean operation, which is faster + +% count vertices +v = sum(img(:)); + +% count edges in each direction +e1 = sum(sum(sum(img(1:N1-1,:,:) & img(2:N1,:,:)))); +e2 = sum(sum(sum(img(:,1:N2-1,:) & img(:,2:N2,:)))); +e3 = sum(sum(sum(img(:,:,1:N3-1,:) & img(:,:,2:N3)))); + +% count square faces orthogonal to each main directions +f1 = sum(sum(sum(... + img(:, 1:N2-1, 1:N3-1) & img(:, 1:N2-1, 2:N3) & ... + img(:, 2:N2, 1:N3-1) & img(:, 2:N2, 2:N3) ))); +f2 = sum(sum(sum(... + img(1:N1-1, :, 1:N3-1) & img(1:N1-1, :, 2:N3) & ... + img(2:N1, :, 1:N3-1) & img(2:N1, :, 2:N3) ))); +f3 = sum(sum(sum(... + img(1:N1-1, 1:N2-1, :) & img(1:N1-1, 2:N2, :) & ... + img(2:N1, 1:N2-1, :) & img(2:N1, 2:N2, :) ))); + +% compute number of cubes +s = sum(sum(sum(... + img(1:N1-1, 1:N2-1, 1:N3-1) & img(1:N1-1, 2:N2, 1:N3-1) & ... + img(2:N1, 1:N2-1, 1:N3-1) & img(2:N1, 2:N2, 1:N3-1) & ... + img(1:N1-1, 1:N2-1, 2:N3) & img(1:N1-1, 2:N2, 2:N3) & ... + img(2:N1, 1:N2-1, 2:N3) & img(2:N1, 2:N2, 2:N3) ))); + +if conn == 6 + % compute euler characteristics using graph formula + chi = v - (e1+e2+e3) + (f1+f2+f3) - s; + return; + +elseif conn == 26 + + % compute EPC inside image, with correction on edges + % Compute the map of 2-by-2-by-2 configurations within image + configMap = img(1:N1-1, 1:N2-1, 1:N3-1) + ... + 2*img(2:N1, 1:N2-1, 1:N3-1) + ... + 4*img(1:N1-1, 2:N2, 1:N3-1) + ... + 8*img(2:N1, 2:N2, 1:N3-1) + ... + 16*img(1:N1-1, 1:N2-1, 2:N3) + ... + 32*img(2:N1, 1:N2-1, 2:N3) + ... + 64*img(1:N1-1, 2:N2, 2:N3) + ... + 128*img(2:N1, 2:N2, 2:N3); + % Compute histogram of configurations + histo = histcounts(configMap(:), 'BinLimits', [0 255], 'BinMethod', 'integers'); + % mutliplies by pre-computed contribution of each configuration + epcc = sum(histo .* eulerLutC26()); + + % Compute edge correction, in order to compensate it and obtain the + % Euler-Poincare characteristic computed for whole image. + + % compute epc on faces % 4 + f10 = imEuler2dC8(squeeze(img(1,:,:))); + f11 = imEuler2dC8(squeeze(img(N1,:,:))); + f20 = imEuler2dC8(squeeze(img(:,1,:))); + f21 = imEuler2dC8(squeeze(img(:,N2,:))); + f30 = imEuler2dC8(img(:,:,1)); + f31 = imEuler2dC8(img(:,:,N3)); + epcf = f10 + f11 + f20 + f21 + f30 + f31; + + % compute epc on edges % 24 + e11 = imEuler1d(img(:,1,1)); + e12 = imEuler1d(img(:,1,N3)); + e13 = imEuler1d(img(:,N2,1)); + e14 = imEuler1d(img(:,N2,N3)); + + e21 = imEuler1d(img(1,:,1)); + e22 = imEuler1d(img(1,:,N3)); + e23 = imEuler1d(img(N1,:,1)); + e24 = imEuler1d(img(N1,:,N3)); + + e31 = imEuler1d(img(1,1,:)); + e32 = imEuler1d(img(1,N2,:)); + e33 = imEuler1d(img(N1,1,:)); + e34 = imEuler1d(img(N1,N2,:)); + + epce = e11 + e12 + e13 + e14 + e21 + e22 + e23 + e24 + ... + e31 + e32 + e33 + e34; + + % compute epc on vertices + epcn = img(1,1,1) + img(1,1,N3) + img(1,N2,1) + img(1,N2,N3) + ... + img(N1,1,1) + img(N1,1,N3) + img(N1,N2,1) + img(N1,N2,N3); + + % compute epc from measurements made on interior of window, and + % facets of lower dimension + chi = epcc + ( epcf/2 - epce/4 + epcn/8); + +else + error('imEuler3d: uknown connectivity option'); +end + +function tab = eulerLutC26 +% Return the pre-computed array of Euler number contribution of each +% 2-by-2-by-2 configuration +tab = [... + 0 1 1 0 1 0 -2 -1 1 -2 0 -1 0 -1 -1 0 ... + 1 0 -2 -1 -2 -1 -1 -2 -6 -3 -3 -2 -3 -2 0 -1 ... + 1 -2 0 -1 -6 -3 -3 -2 -2 -1 -1 -2 -3 0 -2 -1 ... + 0 -1 -1 0 -3 -2 0 -1 -3 0 -2 -1 0 1 1 0 ... + 1 -2 -6 -3 0 -1 -3 -2 -2 -1 -3 0 -1 -2 -2 -1 ... + 0 -1 -3 -2 -1 0 0 -1 -3 0 0 1 -2 -1 1 0 ... + -2 -1 -3 0 -3 0 0 1 -1 4 0 3 0 3 1 2 ... + -1 -2 -2 -1 -2 -1 1 0 0 3 1 2 1 2 2 1 ... + 1 -6 -2 -3 -2 -3 -1 0 0 -3 -1 -2 -1 -2 -2 -1 ... + -2 -3 -1 0 -1 0 4 3 -3 0 0 1 0 1 3 2 ... + 0 -3 -1 -2 -3 0 0 1 -1 0 0 -1 -2 1 -1 0 ... + -1 -2 -2 -1 0 1 3 2 -2 1 -1 0 1 2 2 1 ... + 0 -3 -3 0 -1 -2 0 1 -1 0 -2 1 0 -1 -1 0 ... + -1 -2 0 1 -2 -1 3 2 -2 1 1 2 -1 0 2 1 ... + -1 0 -2 1 -2 1 1 2 -2 3 -1 2 -1 2 0 1 ... + 0 -1 -1 0 -1 0 2 1 -1 2 0 1 0 1 1 0 ... +] / 8; + + +function chi = imEuler2dC8(img) + +% size of image in each direction +dim = size(img); +N1 = dim(1); +N2 = dim(2); + +% compute number of nodes, number of edges (H and V) and number of faces. +% principle is erosion with simple structural elements (line, square) +% but it is replaced here by simple boolean operation, which is faster + +% count vertices +n = sum(img(:)); + +% count horizontal and vertical edges +n1 = sum(sum(img(1:N1-1,:) & img(2:N1,:))); +n2 = sum(sum(img(:,1:N2-1) & img(:,2:N2))); + +% count square faces +n1234 = sum(sum(... + img(1:N1-1,1:N2-1) & img(1:N1-1,2:N2) & ... + img(2:N1,1:N2-1) & img(2:N1,2:N2) )); + +% For 8-connectivity, need also to count diagonal edges +n3 = sum(sum(img(1:N1-1,1:N2-1) & img(2:N1,2:N2))); +n4 = sum(sum(img(1:N1-1,2:N2) & img(2:N1,1:N2-1))); + +% and triangular faces +n123 = sum(sum(img(1:N1-1,1:N2-1) & img(1:N1-1,2:N2) & img(2:N1,1:N2-1) )); +n124 = sum(sum(img(1:N1-1,1:N2-1) & img(1:N1-1,2:N2) & img(2:N1,2:N2) )); +n134 = sum(sum(img(1:N1-1,1:N2-1) & img(2:N1,1:N2-1) & img(2:N1,2:N2) )); +n234 = sum(sum(img(1:N1-1,2:N2) & img(2:N1,1:N2-1) & img(2:N1,2:N2) )); + +% compute Eeuler characteristics from graph counts +% chi = Nvertices - Nedges + Ntriangles + Nsquares +chi = n - (n1+n2+n3+n4) + (n123+n124+n134+n234) - n1234; + + +function chi = imEuler1d(img) +% Compute Euler number of a binary 1D image. +chi = sum(img(:)) - sum(img(1:end-1) & img(2:end)); + diff --git a/src/@Image/regionFeretDiameter.m b/src/@Image/regionFeretDiameter.m new file mode 100644 index 0000000..9187ece --- /dev/null +++ b/src/@Image/regionFeretDiameter.m @@ -0,0 +1,154 @@ +function [fd, labels] = regionFeretDiameter(obj, varargin) +% Feret diameter of region(s) for a given direction. +% +% FD = imFeretDiameter(IMG, THETA); +% Compute the Feret diameter for particles in image IMG (binary or +% label), for the direction THETA, given in degrees. +% The result is a N-by-1 column vector, containing the Feret diameter of +% each particle in IMG. +% +% THETA can be a set of directions. In this case, the result has as many +% columns as the number of directions, and as many rows as the number of +% particles. +% +% FD = imFeretDiameter(IMG); +% Uses a default set of directions (180) for computing Feret diameters. +% +% FD = imFeretDiameter(..., LABELS); +% Specifies the labels for which the Feret diameter should be computed. +% LABELS is a N-by-1 column vector. This can be used to save computation +% time when only few particles / regions are of interset within the +% entire image. +% +% [FD, LABELS] = imFeretDiameter(...); +% Also returns the set of labels that were considered for measure. +% +% The maximum Feret diameter can be obtained using a max() function, or +% by calling the "regionMaxFeretDiameter" function. +% +% Example: +% % compute Feret diameter for a discrete square +% data = zeros(100, 100, 'uint8'); +% data(21:80, 21:80) = 1; +% img = Image(data, 'type', 'binary'); +% theta = linspace(0, 180, 201); +% fd = regionFeretDiameter(img, theta); +% figure(1); clf; set(gca, 'fontsize', 14); +% plot(theta, fd); xlim([0 180]); +% xlabel('Angle (in degrees)'); +% ylabel('Diameter (in pixels)'); +% title('Feret diameter of discrete square'); +% +% % max Feret diameter: +% diam = max(fd, [], 2) +% ans = +% 84.4386 +% +% See also +% regionMaxFeretDiameter, regionOrientedBox +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-10-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Process input arguments + +if ndims(obj) ~= 2 %#ok + error('Requires 2D image as input'); +end + +% Extract number of orientations +theta = 180; +if ~isempty(varargin) + var1 = varargin{1}; + if isscalar(var1) + % Number of directions given as scalar + theta = var1; + varargin(1) = []; + + elseif ndims(var1) == 2 && sum(size(var1) ~= [1 2]) ~= 0 %#ok + % direction set given as vector + theta = var1; + varargin(1) = []; + end +end + + +%% Extract spatial calibration + +% extract calibration +spacing = obj.Spacing; +origin = obj.Origin; +calib = isCalibrated(obj); + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + + +%% Initialisations + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + +% allocate memory for result +nTheta = length(theta); +fd = zeros(nLabels, nTheta); + +% iterate over labels +for i = 1:nLabels + % extract pixel centroids + [x, y] = find(obj.Data == labels(i)); + if isempty(x) + continue; + end + + % transform to physical space if needed + if calib + x = (x-1) * spacing(1) + origin(1); + y = (y-1) * spacing(2) + origin(2); + end + + % keep only points of the convex hull + try + inds = convhull(x, y); + x = x(inds); + y = y(inds); + catch ME %#ok + % an exception can occur if points are colinear. + % in this case we transform all points + end + + % recenter points (should be better for numerical accuracy) + x = x - mean(x); + y = y - mean(y); + + % iterate over orientations + for t = 1:nTheta + % convert angle to radians, and change sign (to make transformed + % points aligned along x-axis) + theta2 = -theta(t) * pi / 180; + + % compute only transformed x-coordinate + x2 = x * cos(theta2) - y * sin(theta2); + + % compute diameter for extreme coordinates + xmin = min(x2); + xmax = max(x2); + + % store result (add 1 pixel to consider pixel width) + dl = spacing(1) * abs(cos(theta2)) + spacing(2) * abs(sin(theta2)); + fd(i, t) = xmax - xmin + dl; + end +end + diff --git a/src/@Image/regionGeodesicDiameter.m b/src/@Image/regionGeodesicDiameter.m new file mode 100644 index 0000000..5bc2d98 --- /dev/null +++ b/src/@Image/regionGeodesicDiameter.m @@ -0,0 +1,233 @@ +function [gd, labels] = regionGeodesicDiameter(obj, varargin) +% Compute geodesic diameter of regions within a label imag. +% +% GD = regionGeodesicDiameter(IMG) +% where IMG is a label image, returns the geodesic diameter of each +% particle in the image. If IMG is a binary image, a connected-components +% labelling is performed first. +% GD is a column vector containing the geodesic diameter of each particle. +% +% GD = regionGeodesicDiameter(IMG, WS) +% Specifies the weights associated to neighbor pixels. WS(1) is the +% distance to orthogonal pixels, and WS(2) is the distance to diagonal +% pixels. An optional WS(3) weight may be specified, corresponding to +% chess-knight moves. Default is [5 7 11], recommended for 5-by-5 masks. +% The final length is normalized by weight for orthogonal pixels. For +% thin structures (skeletonization result), or for very close particles, +% the [3 4] weights recommended by Borgeors may be more appropriate. +% +% GD = regionGeodesicDiameter(..., 'verbose', true); +% Display some informations about the computation procedure, that may +% take some time for large and/or complicated images. +% +% [GD, LABELS] = regionGeodesicDiameter(...); +% Also returns the list of labels for which the geodesic diameter was +% computed. +% +% +% These algorithm uses 3 steps: +% * first propagate distance from region boundary to find a pixel +% approximately in the center of the particle(s) +% * propagate distances from the center, and keep the furthest pixel, +% which is assumed to be a geodesic extremity +% * propagate distances from the geodesic extremity, and keep the maximal +% distance. +% This algorithm is less time-consuming than the direct approach that +% consists in computing geodesic propagation and keeping the max value. +% However, for some cases (e.g. particles with holes) in can happen that +% the two methods give different results. +% +% +% Notes: +% * only planar images are currently supported. +% * the particles are assumed to be 8 connected. If two or more particles +% touch by a corner, the result will not be valid. +% +% +% Example +% % segment and labelize image of grains, and compute their geodesic +% % diameter +% img = Image.read('rice.png'); +% img2 = whiteTopHat(img, ones(30, 30)); +% bin = opening(img2 > 50, ones(3, 3)); +% lbl = componentLabeling(bin); +% gd = regionGeodesicDiameter(lbl); +% plot(gd, '+'); +% +% References +% * Lantuejoul, C. and Beucher, S. (1981): "On the use of geodesic metric +% in image analysis", J. Microsc., 121(1), pp. 39-49. +% http://dx.doi.org/10.1111/j.1365-2818.1981.tb01197.x +% * Coster & Chermant: "Precis d'analyse d'images", Ed. CNRS 1989. +% +% See also +% regionMaxFeretDiameter, geodesicDistanceMap, chamferDistanceMap +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Default values + +% weights for propagating geodesic distances +ws = [5 7 11]; + +% no verbosity by default +verbose = 0; + +labels = []; + + +%% Process input arguments + +% extract weights if present +if ~isempty(varargin) + if isnumeric(varargin{1}) + ws = varargin{1}; + varargin(1) = []; + end +end + +% Extract options +while ~isempty(varargin) + paramName = varargin{1}; + if strcmpi(paramName, 'verbose') + verbose = varargin{2}; + elseif strcmpi(paramName, 'labels') + labels = varargin{2}; + else + error(['Unkown option in regionGeodesicDiameter: ' paramName]); + end + varargin(1:2) = []; +end + +% make input image a label image if this is not the case +if isBinaryImage(obj) + labels = 1; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + + +%% Detection of center point (furthest point from boundary) + +if verbose + disp(sprintf('Computing geodesic diameters of %d region(s).', nLabels)); %#ok<*DSPS> +end + +if verbose + disp('Computing initial centers...'); +end + +% computation of distance map from empirical markers +dist = chamferDistanceMap(obj, ws, 'normalize', false, 'verbose', verbose); + + +%% Second pass: find a geodesic extremity + +if verbose + disp('Create marker image of initial centers'); +end + +% Create arrays to find the pixel with largest distance in each label +maxVals = -ones(nLabels, 1); +maxValInds = zeros(nLabels, 1); + +% iterate over pixels, and compare current distance with max distance +% stored for corresponding label +for i = 1:numel(obj.Data) + label = obj.Data(i); + + if label > 0 + ind = find(labels == label); + if dist.Data(i) > maxVals(ind) + maxVals(ind) = dist.Data(i); + maxValInds(ind) = i; + end + end +end + +% compute new seed point in each label, and use it as new marker +markers = Image.false(size(obj)); +markers.Data(maxValInds) = 1; + +if verbose + disp('Propagate distance from initial centers'); +end + +% recomputes geodesic distance from new markers +dist = geodesicDistanceMap(markers, obj, ws, 'normalize', false, 'verbose', verbose); + + +%% third pass: find second geodesic extremity + +if verbose + disp('Create marker image of first geodesic extremity'); +end + +% reset arrays to find the pixel with largest distance in each label +maxVals = -ones(nLabels, 1); +maxValInds = zeros(nLabels, 1); + +% iterate over pixels to identify second geodesic extremities +for i = 1:numel(obj.Data) + label = obj.Data(i); + if label > 0 + ind = find(labels == label); + if dist.Data(i) > maxVals(ind) + maxVals(ind) = dist.Data(i); + maxValInds(ind) = i; + end + end +end + +% compute new seed point in each label, and use it as new marker +markers = Image.false(size(obj)); +markers.Data(maxValInds) = 1; + +if verbose + disp('Propagate distance from first geodesic extremity'); +end + +% recomputes geodesic distance from new markers +dist = geodesicDistanceMap(markers, obj, ws, 'normalize', false, 'verbose', verbose); + + +%% Final computation of geodesic distances + +if verbose + disp('Compute geodesic diameters'); +end + +% keep max geodesic distance inside each label +if isinteger(ws) + gd = zeros(nLabels, 1, class(ws)); +else + gd = -ones(nLabels, 1, class(ws)); +end + +for i = 1:numel(obj.Data) + label = obj.Data(i); + if label > 0 + ind = find(labels == label); + if dist.Data(i) > gd(ind) + gd(ind) = dist.Data(i); + end + end +end + +% normalize by first weight, and add 1 for taking into account pixel +% thickness +gd = gd / ws(1) + 1; + +% finally, normalize with spatial calibration of image +gd = gd * obj.Spacing(1); diff --git a/src/@Image/regionInscribedBall.m b/src/@Image/regionInscribedBall.m new file mode 100644 index 0000000..c52219a --- /dev/null +++ b/src/@Image/regionInscribedBall.m @@ -0,0 +1,71 @@ +function [ball, labels] = regionInscribedBall(obj, varargin) +% Largest ball inscribed within a region. +% +% BALL = regionInscribedBall(IMG) +% Computes the maximal ball inscribed in a given region of a 3D binary +% image, or within each region of 3D label image. +% +% BALL = regionInscribedBall(..., LABELS) +% Specify the labels for which the inscribed balls needs to be computed. +% The result is a N-by-3 array with as many rows as the number of labels. +% +% Example +% img = Image.false([12 12 12]); +% img(2:10, 2:10, 2:10) = 1; +% ball = regionInscribedBall(img) +% ball = +% 6 6 6 5 +% +% See also +% regionEquivalentEllipsoid, drawSphere, distanceMap, +% regionInscribedCircle +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Process input arguments + +if ndims(obj.Data) ~= 3 + error('Requires a 3D input image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + + +%% Main processing + +% allocate memory for result (3 coords + 1 radius) +ball = zeros(nLabels, 4); + +for iLabel = 1:nLabels + % compute distance map + distMap = distanceMap(obj == labels(iLabel)); + + % find value and position of the maximum + [maxi, inds] = max(distMap.Data(:)); + [yb, xb, zb] = ind2sub(size(distMap), inds); + + ball(iLabel,:) = [xb yb zb maxi]; +end + +% apply spatial calibration +if isCalibrated(obj) + ball(:,1:3) = bsxfun(@plus, bsxfun(@times, ball(:,1:3) - 1, obj.Spacing), obj.Origin); + ball(:,4) = ball(:,4) * obj.Spacing(1); +end diff --git a/src/@Image/regionInscribedCircle.m b/src/@Image/regionInscribedCircle.m new file mode 100644 index 0000000..c05862e --- /dev/null +++ b/src/@Image/regionInscribedCircle.m @@ -0,0 +1,78 @@ +function [circle, labels] = regionInscribedCircle(obj, varargin) +% Largest circle inscribed within a region. +% +% CIRC = regionInscribedCircle(IMG) +% Computes the maximal circle inscribed in a given region of a binary +% image, or within each region of label image. +% +% CIRC = regionInscribedCircle(..., LABELS) +% Specify the labels for which the inscribed circle needs to be computed. +% The result is a N-by-3 array with as many rows as the number of labels. +% +% +% Example +% % Draw a commplex particle together with its enclosing circle +% img = fillHoles(Image.read('circles.png')); +% figure; show(img); hold on; +% circ = regionInscribedCircle(img); +% drawCircle(circ, 'LineWidth', 2) +% +% % Compute and display the equivalent ellipses of several particles +% img = Image.read('rice.png'); +% img2 = whiteTopHat(img, ones(30, 30)); +% lbl = componentLabeling(img2 > 50, 4); +% circles = regionInscribedCircle(lbl); +% figure; show(img); hold on; +% drawCircle(circles, 'LineWidth', 2, 'Color', 'g'); +% +% See also +% regionEquivalentEllipse, drawCircle, distanceMap +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + +%% Process input arguments + +if ~ismatrix(obj.Data) + error('Requires a 2D input image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + + +%% Main processing + +% allocate memory for result +circle = zeros(nLabels, 3); + +for iLabel = 1:nLabels + % compute distance map + distMap = distanceMap(obj == labels(iLabel)); + + % find value and position of the maximum + maxi = max(distMap(:)); + [xc, yc] = find(distMap==maxi, 1, 'first'); + + circle(iLabel,:) = [xc yc maxi]; +end + +% apply spatial calibration +if isCalibrated(obj) + circle(:,1:2) = bsxfun(@plus, bsxfun(@times, circle(:,1:2) - 1, obj.Spacing), obj.Origin); + circle(:,3) = circle(:,3) * obj.Spacing(1); +end diff --git a/src/@Image/regionIsosurface.m b/src/@Image/regionIsosurface.m new file mode 100644 index 0000000..e14652c --- /dev/null +++ b/src/@Image/regionIsosurface.m @@ -0,0 +1,129 @@ +function [res, labels] = regionIsosurface(obj, varargin) +% Generate isosurface of each region within a label image. +% +% MESHES = regionIsosurface(LBL) +% Computes isosurfaces of each region within the label image LBL. +% +% +% Example +% markers = Image.true([50 50 50]); +% n = 100; +% rng(10); +% seeds = randi(50, [n 3]); +% for i = 1:n +% markers(seeds(i,1), seeds(i,2), seeds(i,3)) = false; +% end +% wat = watershed(distanceMap(markers), 6); +% wat2 = killBorders(wat); +% figure; hold on; axis equal; +% regionIsosurface(wat2, 'smoothRadius', 2, 'LineStyle', 'none'); +% view(3), light; +% +% See also +% isosurface, gt +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-02-22, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2021 INRAE. + + +%% Input arguiment processing + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; + varargin(1) = []; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + +% parse other options +smoothRadius = 0; +siz = 0; +if ~isempty(varargin) && strcmpi(varargin{1}, 'smoothRadius') + smoothRadius = varargin{2}; + if isscalar(smoothRadius) + smoothRadius = smoothRadius([1 1 1]); + end + siz = floor(smoothRadius * 2) + 1; + varargin(1:2) = []; +end + + +%% Preprocessing + +% retrieve voxel coordinates in physical space +% (common to all labels) +x = getX(obj); +y = getY(obj); +z = getZ(obj); + +% permute data to comply with Matlab orientation +v = permute(obj.Data(:,:,:,1,1), [2 1 3]); + +% allocate memory for labels +meshes = cell(1, nLabels); + + +%% Isosurface computation + +% iterate over regions to generate isosurfaces +for iLabel = 1:nLabels + label = labels(iLabel); + + vol = double(v == label); + % optional smoothing + if smoothRadius > 0 + vol = filterData(vol, siz, smoothRadius); + end + + meshes{iLabel} = isosurface(x, y, z, vol, 0.5); +end + + +%% Finalization + +if nargout == 0 + % display isosurfaces + for i = 1:nLabels + patch(meshes{i}, varargin{:}); + end + +else + % return computed mesh list + res = meshes; +end + + +%% Utility function +function data = filterData(data, kernelSize, sigma) +% process each direction +for i = 1:3 + % compute spatial reference + refSize = (kernelSize(i) - 1) / 2; + s0 = floor(refSize); + s1 = ceil(refSize); + lx = -s0:s1; + + % compute normalized kernel + sigma2 = 2*sigma(i).^2; + kernel = exp(-(lx.^2 / sigma2)); + kernel = kernel / sum(kernel); + + % reshape the kernel such as it is elongated in the i-th direction + newDim = [ones(1, i-1) kernelSize(i) ones(1, 3-i)]; + kernel = reshape(kernel, newDim); + + % apply filtering along one direction + data = imfilter(data, kernel, 'replicate'); +end + diff --git a/src/@Image/regionIsosurfaces.m b/src/@Image/regionIsosurfaces.m index 7ab4312..bbf6177 100644 --- a/src/@Image/regionIsosurfaces.m +++ b/src/@Image/regionIsosurfaces.m @@ -1,6 +1,8 @@ function [res, labels] = regionIsosurfaces(obj, varargin) % Generate isosurface of each region within a label image. % +% Deprecated: replaced by regionIsosurface +% % MESHES = regionIsosurfaces(LBL) % Computes isosurfaces of each region within the label image LBL. % @@ -30,6 +32,8 @@ % Created: 2021-02-22, using Matlab 9.8.0.1323502 (R2020a) % Copyright 2021 INRAE. +warning('deprecated: replaced by "regionIsosurface" method'); + %% Input arguiment processing diff --git a/src/@Image/regionMaxFeretDiameter.m b/src/@Image/regionMaxFeretDiameter.m new file mode 100644 index 0000000..42469ed --- /dev/null +++ b/src/@Image/regionMaxFeretDiameter.m @@ -0,0 +1,87 @@ +function [diam, thetaMax] = regionMaxFeretDiameter(obj, varargin) +% Maximum Feret diameter of regions within a binary or label image. +% +% FD = regionMaxFeretDiameter(IMG) +% Computes the maximum Feret diameter of particles in label image IMG. +% The result is a N-by-1 column vector, containing the Feret diameter of +% each particle in IMG. +% +% [FD, THETAMAX] = regionMaxFeretDiameter(IMG) +% Also returns the direction for which the diameter is maximal. THETAMAX +% is given in degrees, between 0 and 180. +% +% FD = regionMaxFeretDiameter(IMG, LABELS) +% Specify the labels for which the Feret diameter needs to be computed. +% The result is a N-by-1 array with as many rows as the number of labels. +% +% +% Example +% img = Image.read('circles.png'); +% diam = regionMaxFeretDiameter(img) +% diam = +% 272.7144 +% +% See also +% regionFeretDiameter, regionOrientedBox +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-10-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Process input arguments + +if ndims(obj) ~= 2 %#ok + error('Requires 2D image as input'); +end + +% extract orientations +thetas = 180; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + thetas = varargin{1}; + varargin(1) = []; +end + +if isscalar(thetas) + % assume this is the number of directions to use + thetas = linspace(0, 180, thetas+1); + thetas = thetas(1:end-1); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + + +%% Initialisations + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + +% allocate memory for result +diam = zeros(nLabels, 1); +thetaMax = zeros(nLabels, 1); + + +%% Main processing + +% for each region, compute set of diameters, and keep the max +for i = 1:nLabels + % compute Feret Diameters of current region + diams = regionFeretDiameter(obj == labels(i), thetas); + + % find max diameter, with indices + [diam(i), ind] = max(diams, [], 2); + + % keep max orientation + thetaMax(i) = thetas(ind); +end diff --git a/src/@Image/regionMeanBreadth.m b/src/@Image/regionMeanBreadth.m new file mode 100644 index 0000000..f7fa811 --- /dev/null +++ b/src/@Image/regionMeanBreadth.m @@ -0,0 +1,285 @@ +function [breadth, labels] = regionMeanBreadth(obj, varargin) +% Mean breadth of regions within a 3D binary or label image. +% +% B = regionMeanBreadth(IMG) +% Computes the mean breadth of the binary structure in IMG, or of each +% particle in the label image IMG. +% +% B = regionMeanBreadth(IMG, NDIRS) +% Specifies the number of directions used for estimating the mean breadth +% from the Crofton formula. Can be either 3 (the default) or 13. +% +% B = regionMeanBreadth(..., SPACING) +% Specifies the spatial calibration of the image. SPACING is a 1-by-3 row +% vector containing the voxel size in the X, Y and Z directions, in that +% orders. +% +% [B, LABELS]= regionMeanBreadth(LBL, ...) +% Also returns the set of labels for which the mean breadth was computed. +% +% Example +% % define a ball from its center and a radius (use a slight shift to +% % avoid discretisation artefacts) +% xc = 50.12; yc = 50.23; zc = 50.34; radius = 40.0; +% % Create a discretized image of the ball +% [x y z] = meshgrid(1:100, 1:100, 1:100); +% img = Image(sqrt( (x - xc).^2 + (y - yc).^2 + (z - zc).^2) < radius); +% % compute mean breadth of the ball +% % (expected: the diameter of the ball) +% b = regionMeanBreadth(img) +% b = +% 80 +% +% +% % compute mean breadth of several regions in a label image +% img = Image(zeros([10 10 10], 'uint8'), 'Type', 'Label'); +% img(2:3, 2:3, 2:3) = 1; +% img(5:8, 2:3, 2:3) = 2; +% img(5:8, 5:8, 2:3) = 4; +% img(2:3, 5:8, 2:3) = 3; +% img(5:8, 5:8, 5:8) = 8; +% [breadths, labels] = regionMeanBreadth(img) +% breadths = +% 2.1410 +% 2.0676 +% 2.0676 +% 3.9942 +% 4.9208 +% labels = +% 1 +% 2 +% 3 +% 4 +% 8 +% +% See also +% regionVolume, regionsSurfaceArea, regionsEulerNumber, regionPerimeter +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Parse input arguments + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check dimensionality +nd = ndims(obj); +if nd ~= 3 + error('Requires a 3-dimensional image'); +end + +% default values of parameters +nDirs = 13; +labels = []; + +% Process user input arguments +while ~isempty(varargin) + var1 = varargin{1}; + + if isnumeric(var1) + % option is either connectivity or resolution + if isscalar(var1) + nDirs = var1; + elseif size(var1, 2) == 1 + labels = var1; + end + varargin(1) = []; + + else + error('option should be numeric'); + end + +end + + +%% Process label images + +if isBinaryImage(obj) + % in case of binary image, compute only one label + breadth = meanBreadthBinaryData(obj.Data, nDirs, obj.Spacing); + labels = 1; + +else + % in case of a label image, return a vector with a set of results + + % extract labels if necessary (considers 0 as background) + if isempty(labels) + labels = findRegionLabels(obj); + end + + % allocate result array + nLabels = length(labels); + breadth = zeros(nLabels, 1); + + % compute bounding box of each region + bounds = regionMinMaxIndices(obj, labels); + + % compute perimeter of each region considered as binary image + for i = 1:nLabels + label = labels(i); + + % convert bounding box to image extent, in x and y directions + bx = bounds(i, [1 2]); + by = bounds(i, [3 4]); + bz = bounds(i, [5 6]); + + bin = obj.Data(bx(1):bx(2), by(1):by(2), bz(1):bz(2)) == label; + breadth(i) = meanBreadthBinaryData(bin, nDirs, obj.Spacing); + end +end + + +%% Process Binary data +function breadth = meanBreadthBinaryData(img, nDirs, spacing) + +%% Process binary images + +% pre-compute distances between a pixel and its neighbours. +d1 = spacing(1); +d2 = spacing(2); +d3 = spacing(3); +vol = d1 * d2 * d3; + +%% Main processing for 3 directions + +% number of voxels +nv = sum(img(:)); + +% number of connected components along the 3 main directions +ne1 = sum(sum(sum(img(1:end-1,:,:) & img(2:end,:,:)))); +ne2 = sum(sum(sum(img(:,1:end-1,:) & img(:,2:end,:)))); +ne3 = sum(sum(sum(img(:,:,1:end-1) & img(:,:,2:end)))); + +% number of square faces on plane with normal directions 1 to 3 +nf1 = sum(sum(sum(... + img(:,1:end-1,1:end-1) & img(:,2:end,1:end-1) & ... + img(:,1:end-1,2:end) & img(:,2:end,2:end) ))); +nf2 = sum(sum(sum(... + img(1:end-1,:,1:end-1) & img(2:end,:,1:end-1) & ... + img(1:end-1,:,2:end) & img(2:end,:,2:end) ))); +nf3 = sum(sum(sum(... + img(1:end-1,1:end-1,:) & img(2:end,1:end-1,:) & ... + img(1:end-1,2:end,:) & img(2:end,2:end,:) ))); + +% mean breadth in 3 main directions +b1 = nv - (ne2 + ne3) + nf1; +b2 = nv - (ne1 + ne3) + nf2; +b3 = nv - (ne1 + ne2) + nf3; + +% inverse of planar density (in m = m^3/m^2) in each direction +a1 = vol / (d2 * d3); +a2 = vol / (d1 * d3); +a3 = vol / (d1 * d2); + +if nDirs == 3 + breadth = (b1 * a1 + b2 * a2 + b3 * a3) / 3; + return; +end + + +% number of connected components along the 6 planar diagonal +ne4 = sum(sum(sum(img(1:end-1,1:end-1,:) & img(2:end,2:end,:)))); +ne5 = sum(sum(sum(img(1:end-1,2:end,:) & img(2:end,1:end-1,:)))); +ne6 = sum(sum(sum(img(1:end-1,:,1:end-1) & img(2:end,:,2:end)))); +ne7 = sum(sum(sum(img(1:end-1,:,2:end) & img(2:end,:,1:end-1)))); +ne8 = sum(sum(sum(img(:,1:end-1,1:end-1,:) & img(:,2:end,2:end)))); +ne9 = sum(sum(sum(img(:,1:end-1,2:end,:) & img(:,2:end,1:end-1)))); + +% number of square faces on plane with normal directions 4 to 9 +nf4 = sum(sum(sum(... + img(2:end,1:end-1,1:end-1) & img(1:end-1,2:end,1:end-1) & ... + img(2:end,1:end-1,2:end) & img(1:end-1,2:end,2:end) ))); +nf5 = sum(sum(sum(... + img(1:end-1,1:end-1,1:end-1) & img(2:end,2:end,1:end-1) & ... + img(1:end-1,1:end-1,2:end) & img(2:end,2:end,2:end) ))); + +nf6 = sum(sum(sum(... + img(2:end,1:end-1,1:end-1) & img(2:end,2:end,1:end-1) & ... + img(1:end-1,1:end-1,2:end) & img(1:end-1,2:end,2:end) ))); +nf7 = sum(sum(sum(... + img(1:end-1,1:end-1,1:end-1) & img(1:end-1,2:end,1:end-1) & ... + img(2:end,1:end-1,2:end) & img(2:end,2:end,2:end) ))); + +nf8 = sum(sum(sum(... + img(1:end-1,2:end,1:end-1) & img(2:end,2:end,1:end-1) & ... + img(1:end-1,1:end-1,2:end) & img(2:end,1:end-1,2:end) ))); +nf9 = sum(sum(sum(... + img(1:end-1,1:end-1,1:end-1) & img(2:end,1:end-1,1:end-1) & ... + img(1:end-1,2:end,2:end) & img(2:end,2:end,2:end) ))); + +b4 = nv - (ne5 + ne3) + nf4; +b5 = nv - (ne4 + ne3) + nf5; +b6 = nv - (ne7 + ne2) + nf6; +b7 = nv - (ne6 + ne2) + nf7; +b8 = nv - (ne9 + ne1) + nf8; +b9 = nv - (ne8 + ne1) + nf9; + +% number of triangular faces on plane with normal directions 10 to 13 +nf10 = sum(sum(sum(... + img(2:end,1:end-1,1:end-1) & img(1:end-1,2:end,1:end-1) & ... + img(1:end-1,1:end-1,2:end) ))) ... + + sum(sum(sum(... + img(2:end,2:end,1:end-1) & img(1:end-1,2:end,2:end) & ... + img(2:end,1:end-1,2:end) ))) ; + +nf11 = sum(sum(sum(... + img(1:end-1,1:end-1,1:end-1) & img(2:end,2:end,1:end-1) & ... + img(2:end,1:end-1,2:end) ))) ... + + sum(sum(sum(... + img(1:end-1,2:end,1:end-1) & img(1:end-1,1:end-1,2:end) & ... + img(2:end,2:end,2:end) ))) ; + +nf12 = sum(sum(sum(... + img(1:end-1,1:end-1,1:end-1) & img(2:end,2:end,1:end-1) & ... + img(1:end-1,2:end,2:end) ))) ... + + sum(sum(sum(... + img(2:end,1:end-1,1:end-1) & img(1:end-1,1:end-1,2:end) & ... + img(2:end,2:end,2:end) ))) ; + +nf13 = sum(sum(sum(... + img(2:end,1:end-1,1:end-1) & img(1:end-1,2:end,1:end-1) & ... + img(2:end,2:end,2:end) ))) ... + + sum(sum(sum(... + img(1:end-1,1:end-1,1:end-1) & img(2:end,1:end-1,2:end) & ... + img(1:end-1,2:end,2:end) ))) ; + +% length of diagonals +d12 = hypot(d1, d2); +d13 = hypot(d1, d3); +d23 = hypot(d2, d3); + +% inverse of planar density (in m = m^3/m^2) in directions 4 to 13 +a4 = vol / (d3 * d12); +a6 = vol / (d2 * d13); +a8 = vol / (d1 * d23); + +% compute area of diagonal triangle via Heron's formula +s = (d12 + d13 + d23) / 2; +a10 = vol / (2 * sqrt( s * (s-d12) * (s-d13) * (s-d23) )); + + +b10 = nv - (ne5 + ne7 + ne9) + nf10; +b11 = nv - (ne4 + ne6 + ne9) + nf11; +b12 = nv - (ne4 + ne7 + ne8) + nf12; +b13 = nv - (ne5 + ne6 + ne8) + nf13; + +if nDirs ~= 13 + error('Unknown number of directions'); +end + +c = Image.directionWeights3d13(spacing); + +% weighted average over directions +breadth = ... + (b1*c(1)*a1 + b2*c(2)*a2 + b3*c(3)*a3) + ... + ((b4+b5)*c(4)*a4 + (b6+b7)*c(6)*a6 + (b8+b9)*c(8)*a8) + ... + ((b10 + b11 + b12 + b13)*c(10)*a10) ; diff --git a/src/@Image/regionMinMaxIndices.m b/src/@Image/regionMinMaxIndices.m new file mode 100644 index 0000000..9bd361b --- /dev/null +++ b/src/@Image/regionMinMaxIndices.m @@ -0,0 +1,80 @@ +function [boxes, labels] = regionMinMaxIndices(obj, varargin) +% Bounding indices of regions within a label image, in pixel coordinates. +% +% BI = regionMinMaxIndices(lbl); +% Similar to the regionBoundingBox function, but do not take into account +% spatial calibration of images. +% Returns a set of start and end indices for each dimension and each +% region. +% BI = [INDXMIN INDXMAX INDYMIN INDYMAX] +% +% The region within the imag can be cropped using: +% img2 = img(BI(1,1):BI(1,2), BI(2,1):BI(2,2)); +% +% Example +% % crop an image using the result of regionMinMaxIndices +% img = Image.read('circles.png'); +% bi = regionMinMaxIndices(img); +% img2 = img{bi(1):bi(2), bi(3):bi(4)}; +% figure; +% show(img2) +% +% See also +% drawBox, regionBoundingBox +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-10-19, using Matlab 9.8.0.1323502 (R2020a) +% Copyright 2021 INRAE. + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && isnumeric(varargin{1}) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end + +% switch processing depending on dimension +nd = ndims(obj); +if nd == 2 + %% Process planar case + % compute bounds using regionprops for speed. Result is a struct array. + props = regionprops(obj.Data, 'BoundingBox'); + props = props(labels); + bb = reshape([props.BoundingBox], [4 length(props)])'; + + % round start index + bb(:,[1 2]) = ceil(bb(:,[1 2])); + bb(:,[3 4]) = bb(:,[3 4]) - 1; + + % convert to (x,y) indexing convention + boxes = [bb(:, 2) bb(:, 2)+bb(:, 4) bb(:, 1) bb(:, 1)+bb(:, 3)]; + +elseif nd == 3 + %% Process 3D case + % compute bounds using regionprops3 for speed. Result is a table. + props = regionprops3(obj.Data, 'BoundingBox'); + bb = props.BoundingBox(labels, :); + + % round start index + bb(:,[1 2 3]) = ceil(bb(:,[1 2 3])); + bb(:,[4 5 6]) = bb(:,[4 5 6]) - 1; + + % convert to (x,y,z) indexing convention + boxes = [bb(:, 2) bb(:, 2)+bb(:, 5) bb(:, 1) bb(:, 1)+bb(:, 4) bb(:, 3) bb(:, 3)+bb(:, 6)]; + +else + error('Image dimension must be 2 or 3'); +end diff --git a/src/@Image/regionOrientedBox.m b/src/@Image/regionOrientedBox.m new file mode 100644 index 0000000..af2741e --- /dev/null +++ b/src/@Image/regionOrientedBox.m @@ -0,0 +1,252 @@ +function rect = regionOrientedBox(obj, varargin) +% Minimum-width oriented bounding box of region(s) within image. +% +% OBB = regionOrientedBox(IMG); +% Computes the minimum width oriented bounding box of the region(s) in +% image IMG. IMG is either a binary or a label image. +% The result OBB is a N-by-5 array, containing the center, the length, +% the width, and the orientation of the bounding box of each particle in +% image. The orientation is given in degrees, in the direction of the +% largest box axis. +% +% [OBB, LABELS] = regionOrientedBox(...); +% Also returns the list of region labels for which the bounding box was +% computed. +% +% Example +% % Compute and display the oriented box of several rice grains +% img = Image.read('rice.png'); +% img2 = img - opening(img, ones(30, 30)); +% lbl = componentLabeling(img2 > 50, 4); +% boxes = regionOrientedBox(lbl); +% show(img); hold on; +% drawOrientedBox(boxes, 'linewidth', 2, 'color', 'g'); +% +% See also +% regionFeretDiameter, regionEquivalentEllipse, regionMaxFeretDiameter + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-10-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Process input arguments + +if ndims(obj) ~= 2 %#ok + error('Requires 2D image as input'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + + +%% Initialisations + +%% Extract spatial calibration + +% extract calibration +spacing = obj.Spacing; +origin = obj.Origin; +calib = isCalibrated(obj); + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end +nLabels = length(labels); + +% allocate memory for result +rect = zeros(nLabels, 5); + + +%% Iterate over labels + +for i = 1:nLabels + % extract points of the current region + [x, y] = find(obj.Data == labels(i)); + if isempty(x) + continue; + end + + % transform to physical space if needed + if calib + x = (x-1) * spacing(1) + origin(1); + y = (y-1) * spacing(2) + origin(2); + end + + % special case of regions composed of only one pixel + if length(x) == 1 + rect(i,:) = [x y 1 1 0]; + continue; + end + + % compute bounding box of region pixel centers + try + obox = orientedBox([x y]); + catch ME %#ok + % if points are aligned, convex hull computation fails. + % Perform manual computation of box. + xc = mean(x); + yc = mean(y); + x = x - xc; + y = y - yc; + + theta = mean(mod(atan2(y, x), pi)); + [x2, y2] = transformPoint(x, y, createRotation(-theta)); %#ok + dmin = min(x2); + dmax = max(x2); + center = [(dmin + dmax)/2 0]; + center = transformPoint(center, createRotation(theta)) + [xc yc]; + obox = [center (dmax-dmin) 0 rad2deg(theta)]; + end + + % pre-compute trigonometric functions + thetaMax = obox(5); + cot = cosd(thetaMax); + sit = sind(thetaMax); + + % add a thickness of one pixel in both directions + dsx = spacing(1) * abs(cot) + spacing(2) * abs(sit); + dsy = spacing(1) * abs(sit) + spacing(2) * abs(cot); + obox(3:4) = obox(3:4) + [dsx dsy]; + + % concatenate rectangle data + rect(i,:) = obox; +end + + +function varargout = transformPoint(varargin) +% Apply an affine transform to a point or a point set. +% +% PT2 = transformPoint(PT1, TRANSFO); +% Returns the result of the transformation TRANSFO applied to the point +% PT1. PT1 has the form [xp yp], and TRANSFO is either a 2-by-2, a +% 2-by-3, or a 3-by-3 matrix, +% +% Format of TRANSFO can be one of : +% [a b] , [a b c] , or [a b c] +% [d e] [d e f] [d e f] +% [0 0 1] +% +% PT2 = transformPoint(PT1, TRANSFO); +% Also works when PTA is a N-by-2 array representing point coordinates. +% In this case, the result PT2 has the same size as PT1. +% +% [X2, Y2] = transformPoint(X1, Y1, TRANS); +% Also works when PX1 and PY1 are two arrays the same size. The function +% transforms each pair (PX1, PY1), and returns the result in (X2, Y2), +% which has the same size as (PX1 PY1). +% +% +% See also: +% points2d, transforms2d, translation, rotation +% + +% parse input arguments +if length(varargin) == 2 + var = varargin{1}; + px = var(:,1); + py = var(:,2); + trans = varargin{2}; +elseif length(varargin) == 3 + px = varargin{1}; + py = varargin{2}; + trans = varargin{3}; +else + error('wrong number of arguments in "transformPoint"'); +end + + +% apply linear part of the transform +px2 = px * trans(1,1) + py * trans(1,2); +py2 = px * trans(2,1) + py * trans(2,2); + +% add translation vector, if exist +if size(trans, 2) > 2 + px2 = px2 + trans(1,3); + py2 = py2 + trans(2,3); +end + +% format output arguments +if nargout < 2 + varargout{1} = [px2 py2]; +elseif nargout + varargout{1} = px2; + varargout{2} = py2; +end + + +function trans = createRotation(varargin) +%CREATEROTATION Create the 3*3 matrix of a rotation. +% +% TRANS = createRotation(THETA); +% Returns the rotation corresponding to angle THETA (in radians) +% The returned matrix has the form : +% [cos(theta) -sin(theta) 0] +% [sin(theta) cos(theta) 0] +% [0 0 1] +% +% TRANS = createRotation(POINT, THETA); +% TRANS = createRotation(X0, Y0, THETA); +% Also specifies origin of rotation. The result is similar as performing +% translation(-X0, -Y0), rotation(THETA), and translation(X0, Y0). +% +% Example +% % apply a rotation on a polygon +% poly = [0 0; 30 0;30 10;10 10;10 20;0 20]; +% trans = createRotation([10 20], pi/6); +% polyT = transformPoint(poly, trans); +% % display the original and the rotated polygons +% figure; hold on; axis equal; axis([-10 40 -10 40]); +% drawPolygon(poly, 'k'); +% drawPolygon(polyT, 'b'); +% +% See also: +% transforms2d, transformPoint, createRotation90, createTranslation +% + +% --------- +% author : David Legland +% INRA - TPV URPOI - BIA IMASTE +% created the 06/04/2004. +% + +% HISTORY +% 22/04/2009: rename as createRotation + +% default values +cx = 0; +cy = 0; +theta = 0; + +% get input values +if length(varargin)==1 + % only angle + theta = varargin{1}; +elseif length(varargin)==2 + % origin point (as array) and angle + var = varargin{1}; + cx = var(1); + cy = var(2); + theta = varargin{2}; +elseif length(varargin)==3 + % origin (x and y) and angle + cx = varargin{1}; + cy = varargin{2}; + theta = varargin{3}; +end + +% compute coefs +cot = cos(theta); +sit = sin(theta); +tx = cy*sit - cx*cot + cx; +ty = -cy*cot - cx*sit + cy; + +% create transformation matrix +trans = [cot -sit tx; sit cot ty; 0 0 1]; diff --git a/src/@Image/regionPerimeter.m b/src/@Image/regionPerimeter.m new file mode 100644 index 0000000..90a77be --- /dev/null +++ b/src/@Image/regionPerimeter.m @@ -0,0 +1,225 @@ +function [perim, labels] = regionPerimeter(obj, varargin) +% Perimeter of regions within a 2D binary or label image. +% +% P = imPerimeter(IMG); +% Return an estimate of the perimeter of the image, computed by +% counting intersections with 2D lines, and using discretized version of +% the Crofton formula. +% +% P = imPerimeter(IMG, NDIRS); +% Specify number of directions to use. Use either 2 or 4 (the default). +% +% [P, LABELS] = imPerimeter(LBL, ...) +% Process a label image, and return also the labels for which a value was +% computed. +% +% Example +% % compute the perimeter of a binary disk of radius 40 +% lx = 1:100; ly = 1:100; +% [x, y] = meshgrid(lx, ly); +% img = Image(hypot(x - 50.12, y - 50.23) < 40); +% regionPerimeter(img) +% ans = +% 251.1751 +% % to be compared to (2 * pi * 40), approximately 251.3274 +% +% See also +% regionArea, regionEulerNumber, regionSurfaceArea, regionprops +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Parse input arguments + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check dimensionality +nd = ndims(obj); +if nd ~= 2 + error('Requires a 2-dimensional image'); +end + +% default values of parameters +nDirs = 4; +%delta = [1 1]; +labels = []; + +% parse parameter name-value pairs +while ~isempty(varargin) + var1 = varargin{1}; + + if isnumeric(var1) + % option can be number of directions, or list of labels + if isscalar(var1) + nDirs = var1; + elseif size(var1, 2) == 1 + labels = var1; + end + varargin(1) = []; + + elseif ischar(var1) + if length(varargin) < 2 + error('Parameter name must be followed by parameter value'); + end + + if strcmpi(var1, 'ndirs') + nDirs = varargin{2}; + elseif strcmpi(var1, 'Labels') + labels = var1; + else + error(['Unknown parameter name: ' var1]); + end + + varargin(1:2) = []; + end +end + + +%% Process label images + +if isBinaryImage(obj) + % in case of binary image, compute only one label + perim = perimeterBinaryData(obj.Data, nDirs, obj.Spacing); + labels = 1; + +else + % in case of a label image, return a vector with a set of results + + % extract labels if necessary (considers 0 as background) + if isempty(labels) + labels = findRegionLabels(obj); + end + + % allocate result array + nLabels = length(labels); + perim = zeros(nLabels, 1); + + % compute bounding box of each region + bounds = regionMinMaxIndices(obj, labels); + + % compute perimeter of each region considered as binary image + for i = 1:nLabels + label = labels(i); + + % convert bounding box to image extent, in x and y directions + bx = bounds(i, [1 2]); + by = bounds(i, [3 4]); + + bin = obj.Data(bx(1):bx(2), by(1):by(2)) == label; + perim(i) = perimeterBinaryData(bin, nDirs, obj.Spacing); + end +end + + +%% Process 2D binary image +function perim = perimeterBinaryData(img, nDirs, spacing) + +%% Initialisations + +% distances between a pixel and its neighbours (orthogonal, and diagonal) +% (d1 is dx, d2 is dy) +d1 = spacing(1); +d2 = spacing(2); +d12 = hypot(d1, d2); + +% area of a pixel (used for computing line densities) +vol = d1 * d2; + +% size of image +D1 = size(img, 1); +D2 = size(img, 2); + + +%% Processing for 2 or 4 main directions + +% compute number of pixels, equal to the total number of vertices in graph +% reconstructions +nv = sum(img(:)); + +% compute number of connected components along orthogonal lines +% (Use Graph-based formula: chi = nVertices - nEdges) +n1 = nv - sum(sum(img(1:D1-1, :) & img(2:D1, :))); +n2 = nv - sum(sum(img(:, 1:D2-1) & img(:, 2:D2))); + +% Compute perimeter using 2 directions +% equivalent to: +% perim = mean([n1/(d1/a) n2/(d2/a)]) * pi/2; +% with a = d1*d2 being the area of the unit tile +if nDirs == 2 + perim = pi * mean([n1*d2 n2*d1]); + return; +end + + +%% Processing specific to 4 directions + +% Number of connected components along diagonal lines +n3 = nv - sum(sum(img(1:D1-1, 1:D2-1) & img(2:D1, 2:D2))); +n4 = nv - sum(sum(img(1:D1-1, 2:D2 ) & img(2:D1, 1:D2-1))); + +% compute direction weights (necessary for anisotropic case) +if any(d1 ~= d2) + c = computeDirectionWeights2d4([d1 d2])'; +else + c = [1 1 1 1] * 0.25; +end + +% compute weighted average over directions +perim = pi * sum( [n1/d1 n2/d2 n3/d12 n4/d12] * vol .* c ); + + +%% Compute direction weights for 4 directions +function c = computeDirectionWeights2d4(delta) +%COMPUTEDIRECTIONWEIGHTS2D4 Direction weights for 4 directions in 2D +% +% C = computeDirectionWeights2d4 +% Returns an array of 4-by-1 values, corresponding to directions: +% [+1 0] +% [ 0 +1] +% [+1 +1] +% [-1 +1] +% +% C = computeDirectionWeights2d4(DELTA) +% With DELTA = [DX DY]. +% +% Example +% computeDirectionWeights2d4 +% +% See also +% +% +% ------ +% Author: David Legland +% e-mail: david.legland@grignon.inra.fr +% Created: 2010-10-18, using Matlab 7.9.0.529 (R2009b) +% Copyright 2010 INRA - Cepia Software Platform. + +% check case of empty argument +if nargin == 0 + delta = [1 1]; +end + +% angle of the diagonal +theta = atan2(delta(2), delta(1)); + +% angular sector for direction 1 ([1 0]) +alpha1 = theta; + +% angular sector for direction 2 ([0 1]) +alpha2 = (pi/2 - theta); + +% angular sector for directions 3 and 4 ([1 1] and [-1 1]) +alpha34 = pi/4; + +% concatenate the different weights +c = [alpha1 alpha2 alpha34 alpha34]' / pi; + diff --git a/src/@Image/regionSurfaceArea.m b/src/@Image/regionSurfaceArea.m new file mode 100644 index 0000000..5f95432 --- /dev/null +++ b/src/@Image/regionSurfaceArea.m @@ -0,0 +1,245 @@ +function [surf, labels] = regionSurfaceArea(obj, varargin) +% Surface area of the regions within a 3D binary or label image. +% +% S = regionSurfaceArea(IMG) +% Estimates the surface area of the 3D binary structure represented by +% IMG. +% +% S = regionSurfaceArea(IMG, NDIRS) +% Specifies the number of directions used for estimating surface area. +% NDIRS can be either 3 or 13, default is 13. +% +% S = regionSurfaceArea(..., SPACING) +% Specifies the spatial calibration of the image. SPACING is a 1-by-3 row +% vector containing the voxel size in the X, Y and Z directions, in that +% orders. +% +% S = regionSurfaceArea(LBL) +% [S, L] = imSurface(LBL) +% When LBL is a label image, returns the surface area of each label in +% the 3D array, and eventually returns the indices of processed labels. +% +% S = regionSurfaceArea(..., LABELS) +% In the case of a label image, specifies the labels of the region to +% analyse. +% +% +% Example +% % Create a binary image of a ball +% [x y z] = meshgrid(1:100, 1:100, 1:100); +% img = Image(sqrt( (x-50.12).^2 + (y-50.23).^2 + (z-50.34).^2) < 40); +% % compute surface area of the ball +% S = regionSurfaceArea(img) +% S = +% 2.0103e+04 +% % compare with theoretical value +% Sth = 4*pi*40^2; +% 100 * (S - Sth) / Sth +% ans = +% -0.0167 +% +% % compute surface area of several regions in a label image +% img = Image(zeros([10 10 10]), 'Type', 'Label'); +% img(2:3, 2:3, 2:3) = 1; +% img(5:8, 2:3, 2:3) = 2; +% img(5:8, 5:8, 2:3) = 4; +% img(2:3, 5:8, 2:3) = 3; +% img(5:8, 5:8, 5:8) = 8; +% [surfs, labels] = regionSurfaceArea(img) +% surfs = +% 16.4774 +% 29.1661 +% 29.1661 +% 49.2678 +% 76.7824 +% labels = +% 1 +% 2 +% 3 +% 4 +% 8 +% +% +% See also +% regionVolume, regionMeanBreadth, regionEulerNumber, regionPerimeter +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + + +%% Parse input arguments + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check dimensionality +nd = ndims(obj); +if nd ~= 3 + error('Requires a 3-dimensional image'); +end + +% default values of parameters +nDirs = 13; +labels = []; +% methods to compute direction weights. Can be {'Voronoi'}, 'isotropic'. +directionWeights = 'voronoi'; + +% Process user input arguments +while ~isempty(varargin) + var1 = varargin{1}; + + if isnumeric(var1) + % option is either connectivity or resolution + if isscalar(var1) + nDirs = var1; + elseif size(var1, 2) == 1 + labels = var1; + end + varargin(1) = []; + + elseif ischar(var1) + if length(varargin) < 2 + error('optional named argument require a second argument as value'); + end + if strcmpi(var1, 'directionweights') + directionWeights = varargin{2}; + end + varargin(1:2) = []; + + else + error('option should be numeric'); + end + +end + + +%% Process label images + +if isBinaryImage(obj) + % in case of binary image, compute only one label + surf = surfaceAreaBinaryData(obj.Data, nDirs, obj.Spacing, directionWeights); + labels = 1; + +else + % in case of a label image, return a vector with a set of results + + % extract labels if necessary (considers 0 as background) + if isempty(labels) + labels = findRegionLabels(obj); + end + + % allocate result array + nLabels = length(labels); + surf = zeros(nLabels, 1); + + % compute bounding box of each region + bounds = regionMinMaxIndices(obj, labels); + + % compute perimeter of each region considered as binary image + for i = 1:nLabels + label = labels(i); + + % convert bounding box to image extent, in x and y directions + bx = bounds(i, [1 2]); + by = bounds(i, [3 4]); + bz = bounds(i, [5 6]); + + bin = obj.Data(bx(1):bx(2), by(1):by(2), bz(1):bz(2)) == label; + surf(i) = surfaceAreaBinaryData(bin, nDirs, obj.Spacing, directionWeights); + end +end + + +%% Process Binary data +function surf = surfaceAreaBinaryData(img, nDirs, spacing, directionWeights) + +% distances between a pixel and its neighbours. +d1 = spacing(1); % x +d2 = spacing(2); % y +d3 = spacing(3); % z + +% volume of a voxel (used for computing line densities) +vol = d1 * d2 * d3; + + +%% Main processing for 3 directions + +% number of voxels +nv = sum(img(:)); + +% number of connected components along the 3 main directions +% (Use Graph-based formula: chi = nVertices - nEdges) +n1 = nv - sum(sum(sum(img(1:end-1,:,:) & img(2:end,:,:)))); % x +n2 = nv - sum(sum(sum(img(:,1:end-1,:) & img(:,2:end,:)))); % y +n3 = nv - sum(sum(sum(img(:,:,1:end-1) & img(:,:,2:end)))); % z + +if nDirs == 3 + % compute surface area by averaging over the 3 main directions + surf = 4/3 * (n1/d1 + n2/d2 + n3/d3) * vol; + return; +end + + +%% Additional processing for 13 directions + +% Number of connected components along diagonals contained in the three +% main planes +% XY planes +n4 = nv - sum(sum(sum(img(2:end,1:end-1,:) & img(1:end-1,2:end,:)))); +n5 = nv - sum(sum(sum(img(1:end-1,1:end-1,:) & img(2:end,2:end,:)))); +% XZ planes +n6 = nv - sum(sum(sum(img(2:end,:,1:end-1) & img(1:end-1,:,2:end)))); +n7 = nv - sum(sum(sum(img(1:end-1,:,1:end-1) & img(2:end,:,2:end)))); +% YZ planes +n8 = nv - sum(sum(sum(img(:,2:end,1:end-1) & img(:,1:end-1,2:end)))); +n9 = nv - sum(sum(sum(img(:,1:end-1,1:end-1) & img(:,2:end,2:end)))); +% % XZ planes +% n6 = nv - sum(sum(sum(img(:,2:end,1:end-1) & img(:,1:end-1,2:end)))); +% n7 = nv - sum(sum(sum(img(:,1:end-1,1:end-1) & img(:,2:end,2:end)))); +% % YZ planes +% n8 = nv - sum(sum(sum(img(2:end,:,1:end-1) & img(1:end-1,:,2:end)))); +% n9 = nv - sum(sum(sum(img(1:end-1,:,1:end-1) & img(2:end,:,2:end)))); + +% Number of connected components along lines corresponding to diagonals of +% the unit cube +n10 = nv - sum(sum(sum(img(1:end-1,1:end-1,1:end-1) & img(2:end,2:end,2:end)))); +n11 = nv - sum(sum(sum(img(2:end,1:end-1,1:end-1) & img(1:end-1,2:end,2:end)))); +n12 = nv - sum(sum(sum(img(1:end-1,2:end,1:end-1) & img(2:end,1:end-1,2:end)))); +n13 = nv - sum(sum(sum(img(2:end,2:end,1:end-1) & img(1:end-1,1:end-1,2:end)))); + +% space between 2 voxels in each direction +d12 = hypot(d1, d2); +d13 = hypot(d1, d3); +d23 = hypot(d2, d3); +d123 = sqrt(d1^2 + d2^2 + d3^2); + +% Compute weights corresponding to surface fraction of spherical caps +if strcmp(directionWeights, 'isotropic') + c = zeros(13,1); + c(1) = 0.04577789120476 * 2; % Ox + c(2) = 0.04577789120476 * 2; % Oy + c(3) = 0.04577789120476 * 2; % Oz + c(4:5) = 0.03698062787608 * 2; % Oxy + c(6:7) = 0.03698062787608 * 2; % Oxz + c(8:9) = 0.03698062787608 * 2; % Oyz + c(10:13) = 0.03519563978232 * 2; % Oxyz + +else + c = Image.directionWeights3d13(spacing); +end + +% compute the weighted sum of each direction +% intersection count * direction weight / line density +surf = 4 * vol * (... + n1*c(1)/d1 + n2*c(2)/d2 + n3*c(3)/d3 + ... + (n4+n5)*c(4)/d12 + (n6+n7)*c(6)/d13 + (n8+n9)*c(8)/d23 + ... + (n10 + n11 + n12 + n13)*c(10)/d123 ); + + diff --git a/src/@Image/regionVolume.m b/src/@Image/regionVolume.m new file mode 100644 index 0000000..1e487ef --- /dev/null +++ b/src/@Image/regionVolume.m @@ -0,0 +1,56 @@ +function [vol, labels] = regionVolume(obj, varargin) +% Volume of regions within a 3D binary or label image. +% +% V = regionVolume(IMG); +% Computes the volume of the region(s) within the image. IMG is either a +% binary image, or a label image. In the case of a label image, the area +% of each region is returned in a column vector with as many elements as +% the number of labels. +% +% +% Example +% % compute the volume of a binary ball of radius 10 +% lx = 1:30; ly = 1:30; lz = 1:30; +% [x, y, z] = meshgrid(lx, ly, lz); +% img = Image(hypot(hypot(x - 15.12, y - 15.23), z - 15.34) < 40); +% v = regionVolume(img) +% v = +% 4187 +% % to be compared to (4 * pi * 10 ^ 3 / 3), approximately 4188.79 +% +% See also +% regionSurfaceArea, regionEulerNumber, regionArea, regionElementCount +% + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% INRAE - BIA Research Unit - BIBS Platform (Nantes) +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE. + +% check image type +if ~(isLabelImage(obj) || isBinaryImage(obj)) + error('Requires a label of binary image'); +end + +% check dimensionality +nd = ndims(obj); +if nd ~= 3 + error('Requires a 3-dimensional image'); +end + +% check if labels are specified +labels = []; +if ~isempty(varargin) && size(varargin{1}, 2) == 1 + labels = varargin{1}; +end + +% extract the set of labels, without the background +if isempty(labels) + labels = findRegionLabels(obj); +end + +% count the number of elements, and multiply by voxel volume +pixelCounts = regionElementCount(obj, labels); +vol = pixelCounts * prod(obj.Spacing(1:3)); diff --git a/src/@Image/regionalMaxima.m b/src/@Image/regionalMaxima.m index af7dd14..73316b1 100644 --- a/src/@Image/regionalMaxima.m +++ b/src/@Image/regionalMaxima.m @@ -21,24 +21,14 @@ error('Requires a Grayscale or intensity image to work'); end -% default values -conn = 4; +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); -if obj.Dimension == 3 - conn = 6; +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; end -% process input arguments -while ~isempty(varargin) - var = varargin{1}; - - if isnumeric(var) && isscalar(var) - % extract connectivity - conn = var; - varargin(1) = []; - continue; - end -end data = imregionalmax(obj.Data, conn); diff --git a/src/@Image/regionalMinima.m b/src/@Image/regionalMinima.m index 63f7676..5436835 100644 --- a/src/@Image/regionalMinima.m +++ b/src/@Image/regionalMinima.m @@ -21,24 +21,14 @@ error('Requires a Grayscale or intensity image to work'); end -% default values -conn = 4; +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); -if obj.Dimension == 3 - conn = 6; +% case of connectivity specified by user +if ~isempty(varargin) + conn = varargin{1}; end -% process input arguments -while ~isempty(varargin) - var = varargin{1}; - - if isnumeric(var) && isscalar(var) - % extract connectivity - conn = var; - varargin(1) = []; - continue; - end -end data = imregionalmin(obj.Data, conn); diff --git a/src/@Image/sampleFiles/wheatGrainSlice.tif b/src/@Image/sampleFiles/wheatGrainSlice.tif new file mode 100644 index 0000000..16f8acd Binary files /dev/null and b/src/@Image/sampleFiles/wheatGrainSlice.tif differ diff --git a/src/@Image/show.m b/src/@Image/show.m index ec76447..30fe6c0 100644 --- a/src/@Image/show.m +++ b/src/@Image/show.m @@ -14,7 +14,10 @@ % parse options options = {}; while length(varargin) > 1 - if strcmp(varargin{1}, 'showAxisNames') + if strcmpi(varargin{1}, 'showAxisNames') + showAxisNames = varargin{2}; + elseif strcmpi(varargin{1}, 'showTitle') + showTitle = varargin{2}; else options = [options, varargin(1:2)]; %#ok end @@ -49,6 +52,10 @@ xdata = xData(obj); ydata = yData(obj); +% get axis bounds before image display +xl = xlim; +yl = ylim; + %% Display data @@ -57,9 +64,7 @@ % check extent of image extent = physicalExtent(obj); -xl = xlim; xl = [min(xl(1), extent(1)) max(xl(2), extent(2))]; -yl = ylim; yl = [min(yl(1), extent(3)) max(yl(2), extent(4))]; xlim(xl); ylim(yl); diff --git a/src/@Image/watershed.m b/src/@Image/watershed.m index 9a78bb6..a941a14 100644 --- a/src/@Image/watershed.m +++ b/src/@Image/watershed.m @@ -42,10 +42,10 @@ % default values dyn = []; marker = []; -conn = 4; -if obj.Dimension == 3 - conn = 6; -end + +% choose default connectivity depending on dimension +conn = defaultConnectivity(obj); + % process input arguments while ~isempty(varargin) diff --git a/tests/image/files/ellipsoid_Center30x27x25_Size20x12x8_Orient40x30x20.tif b/tests/image/files/ellipsoid_Center30x27x25_Size20x12x8_Orient40x30x20.tif new file mode 100644 index 0000000..eaf8d32 Binary files /dev/null and b/tests/image/files/ellipsoid_Center30x27x25_Size20x12x8_Orient40x30x20.tif differ diff --git a/tests/image/test_catChannels.m b/tests/image/test_catChannels.m index 14f1e78..341cc93 100644 --- a/tests/image/test_catChannels.m +++ b/tests/image/test_catChannels.m @@ -24,5 +24,5 @@ function test_Simple(testCase) %#ok<*DEFNU> res = catChannels(img, img, invert(img)); -assertEqual(3, channelCount(res)); +assertEqual(testCase, 3, channelCount(res)); assertEqual(testCase, size(img), size(res)); diff --git a/tests/image/test_catFrames.m b/tests/image/test_catFrames.m index b5e4018..b9a442c 100644 --- a/tests/image/test_catFrames.m +++ b/tests/image/test_catFrames.m @@ -24,5 +24,5 @@ function test_Simple(testCase) %#ok<*DEFNU> res = catFrames(img, img, invert(img), invert(img), img); -assertEqual(5, frameCount(res)); +assertEqual(testCase, 5, frameCount(res)); assertEqual(testCase, size(img), size(res, 1:2)); diff --git a/tests/image/test_chamferDistanceMap.m b/tests/image/test_chamferDistanceMap.m new file mode 100644 index 0000000..1b724f9 --- /dev/null +++ b/tests/image/test_chamferDistanceMap.m @@ -0,0 +1,77 @@ +function tests = test_chamferDistanceMap +% Test suite for the file chamferDistanceMap. +% +% Test suite for the file chamferDistanceMap +% +% Example +% test_chamferDistanceMap +% +% See also +% chamferDistanceMap + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_SimpleBinary(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +% generate an image of a 10x10 square, with one pixel border +img = Image.false([12, 12]); +img(2:11, 2:11) = 1; + +% compute distance map +distMap = chamferDistanceMap(img); + +assertEqual(testCase, size(img), size(distMap)); +% distance equal to 1 on the borders of the square +assertEqual(testCase, 1, distMap(2, 5)); +assertEqual(testCase, 1, distMap(11, 5)); +assertEqual(testCase, 1, distMap(5, 2)); +assertEqual(testCase, 1, distMap(5, 11)); +% distance equal to 5 in the middle of the square +assertEqual(testCase, 5, distMap(6, 6)); + + +function test_TouchingLabels(testCase) +% Aim is to compute distance map within each label, even if some of them +% touch each other. +% Uses an image with a completely landlocked label region. + +data = [... + 0 0 0 0 0 0 0 0 0 0 0; ... + 0 1 1 1 2 2 2 3 3 3 0; ... + 0 1 1 1 2 2 2 3 3 3 0; ... + 0 1 1 1 2 2 2 3 3 3 0; ... + 0 1 1 1 4 4 4 3 3 3 0; ... + 0 1 1 1 4 4 4 3 3 3 0; ... + 0 1 1 1 4 4 4 3 3 3 0; ... + 0 1 1 1 5 5 5 3 3 3 0; ... + 0 1 1 1 5 5 5 3 3 3 0; ... + 0 1 1 1 5 5 5 3 3 3 0; ... + 0 0 0 0 0 0 0 0 0 0 0; ... +]; +img = Image(data, 'type', 'label'); + + +distMap = chamferDistanceMap(img); + +exp = Image([... + 0 0 0 0 0 0 0 0 0 0 0; ... + 0 1 1 1 1 1 1 1 1 1 0; ... + 0 1 2 1 1 2 1 1 2 1 0; ... + 0 1 2 1 1 1 1 1 2 1 0; ... + 0 1 2 1 1 1 1 1 2 1 0; ... + 0 1 2 1 1 2 1 1 2 1 0; ... + 0 1 2 1 1 1 1 1 2 1 0; ... + 0 1 2 1 1 1 1 1 2 1 0; ... + 0 1 2 1 1 2 1 1 2 1 0; ... + 0 1 1 1 1 1 1 1 1 1 0; ... + 0 0 0 0 0 0 0 0 0 0 0; ... +]); +assertEqual(testCase, size(img), size(distMap)); +assertEqual(testCase, distMap.Data, exp.Data); diff --git a/tests/image/test_geodesicDistanceMap.m b/tests/image/test_geodesicDistanceMap.m index 770e7e8..c1fba97 100644 --- a/tests/image/test_geodesicDistanceMap.m +++ b/tests/image/test_geodesicDistanceMap.m @@ -29,7 +29,7 @@ function test_MarkerAtUpperLeftCorner_10x12(testCase) %#ok<*DEFNU> dist = geodesicDistanceMap(marker, mask, [1 0]); maxDist = max(dist(mask)); -assertTrue(isfinite(maxDist)); +assertTrue(testCase, isfinite(maxDist)); expDist = 10+12-2; assertEqual(testCase, expDist, maxDist); @@ -44,7 +44,7 @@ function test_MarkerAtBottomRightCorner_10x12(testCase) %#ok<*DEFNU> dist = geodesicDistanceMap(marker, mask, [1 0]); maxDist = max(dist(mask)); -assertTrue(isfinite(maxDist)); +assertTrue(testCase, isfinite(maxDist)); expDist = 10+12-2; assertEqual(testCase, expDist, maxDist); @@ -60,7 +60,7 @@ function test_MarkerAtUpperLeftCorner_10x30(testCase) %#ok<*DEFNU> dist = geodesicDistanceMap(marker, mask, [1 1]); maxDist = max(dist(mask)); -assertTrue(isfinite(maxDist)); +assertTrue(testCase, isfinite(maxDist)); expDist = 29; assertEqual(testCase, expDist, maxDist); @@ -76,7 +76,7 @@ function test_MarkerAtBottomRightCorner_10x30(testCase) %#ok<*DEFNU> dist = geodesicDistanceMap(marker, mask, [1 1]); maxDist = max(dist(mask)); -assertTrue(isfinite(maxDist)); +assertTrue(testCase, isfinite(maxDist)); expDist = 29; assertEqual(testCase, expDist, maxDist); @@ -102,4 +102,4 @@ function test_FiniteDistWithMarkerOutside(testCase) dist = imGeodesicDistanceMap(marker, mask); maxDist = max(dist(isfinite(dist))); -assertTrue(isfinite(maxDist)); +assertTrue(testCase, isfinite(maxDist)); diff --git a/tests/image/test_max.m b/tests/image/test_max.m index 4fedf1a..a2bea2a 100644 --- a/tests/image/test_max.m +++ b/tests/image/test_max.m @@ -43,7 +43,7 @@ function test_2d_color_2(testCase) img = Image.read('peppers.png'); res = max(img, 50); -assertTrue(isa(res, 'Image')); +assertTrue(testCase, isa(res, 'Image')); assertEqual(testCase, [50 50 50], min(res)); diff --git a/tests/image/test_medianFilter.m b/tests/image/test_medianFilter.m new file mode 100644 index 0000000..6470cba --- /dev/null +++ b/tests/image/test_medianFilter.m @@ -0,0 +1,49 @@ +function tests = test_medianFilter +% Test suite for the file medianFilter. +% +% Test suite for the file medianFilter +% +% Example +% test_medianFilter +% +% See also +% medianFilter + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-19, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_2d_Size(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +img = Image.read('rice.png'); + +res = medianFilter(img, [3 3]); + +assertEqual(testCase, size(res), size(img)); + + +function test_2d_array(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +img = Image.read('rice.png'); + +se = [0 1 0;1 1 1;0 1 0]; +res = medianFilter(img, se); + +assertEqual(testCase, size(res), size(img)); + + +function test_3d_Size(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +img = Image(ones([7 7 7])); + +res = medianFilter(img, [3 3 3]); + +assertEqual(testCase, size(res), size(img)); + diff --git a/tests/image/test_min.m b/tests/image/test_min.m index ab6a28b..b757f91 100644 --- a/tests/image/test_min.m +++ b/tests/image/test_min.m @@ -43,7 +43,7 @@ function test_2d_color_2(testCase) img = Image.read('peppers.png'); res = min(img, 50); -assertTrue(isa(res, 'Image')); +assertTrue(testCase, isa(res, 'Image')); assertEqual(testCase, [50 50 50], max(res)); diff --git a/tests/image/test_plus.m b/tests/image/test_plus.m index 31dee10..be2bdd0 100644 --- a/tests/image/test_plus.m +++ b/tests/image/test_plus.m @@ -33,7 +33,7 @@ function test_AddConstant(testCase) exp = Image.create([3 4 5 6; 7 8 9 10; 11 12 13 14]); res = img1 + 2; -assertElementsAlmostEqual(exp.Data, res.Data); +assertEqual(testCase, exp.Data, res.Data); res = 2 + img1; assertEqual(testCase, exp.Data, res.Data); diff --git a/tests/image/test_read.m b/tests/image/test_read.m index 459cee7..74413d3 100644 --- a/tests/image/test_read.m +++ b/tests/image/test_read.m @@ -30,3 +30,18 @@ function test_read_color2d(testCase) assertEqual(testCase, 2, ndims(img)); + +function test_read_sampleFile(testCase) %#ok<*DEFNU> + +img = Image.read('wheatGrainSlice.tif'); + +assertEqual(testCase, [340 340], size(img)); + + + +function test_read_sampleFile_noExtension(testCase) %#ok<*DEFNU> + +img = Image.read('wheatGrainSlice'); + +assertEqual(testCase, [340 340], size(img)); + diff --git a/tests/image/test_regionArea.m b/tests/image/test_regionArea.m new file mode 100644 index 0000000..35d0ba3 --- /dev/null +++ b/tests/image/test_regionArea.m @@ -0,0 +1,74 @@ +function tests = test_regionArea +% Test suite for the file regionArea. +% +% Test suite for the file regionArea +% +% Example +% test_regionArea +% +% See also +% regionArea + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + + +function testSquare(testCase) + +img = Image.false(10, 10); +img(3:3+4, 4:4+4) = true; + +a = regionArea(img); + +assertEqual(testCase, 25, a); + + +function testDelta(testCase) +% Test with a non uniform resolution + +img = Image.false(10, 10); +img(3:3+2, 4:4+3) = true; +delta = [3 5]; +img.Spacing = delta; + +a = regionArea(img, delta); + +expectedArea = 3*delta(1) * 4*delta(2); +assertEqual(testCase, expectedArea, a); + + +function testLabel(testCase) + +% create image with 5 different regions +data = [... + 1 1 1 1 2 2 2 2 ; ... + 1 1 1 1 2 2 2 2 ; ... + 1 1 3 3 3 3 2 2 ; ... + 1 1 3 3 3 3 2 2 ; ... + 4 4 3 3 3 3 5 5 ; ... + 4 4 3 3 3 3 5 5 ; ... + 4 4 4 4 5 5 5 5 ; ... + 4 4 4 4 5 5 5 5 ]; +img = Image(data, 'Type', 'label'); + +a = regionArea(img); + +assertEqual(testCase, length(a), 5); +assertEqual(testCase, numel(data), sum(a)); + + +function testLabelImage(testCase) + +img = Image.read('coins.png'); +lbl = componentLabeling(img > 100); + +a = regionArea(lbl); + +assertEqual(testCase, 10, length(a)); +assertTrue(min(a) > 1500); +assertTrue(max(a) < 3000); diff --git a/tests/image/test_regionCentroids.m b/tests/image/test_regionCentroid.m similarity index 75% rename from tests/image/test_regionCentroids.m rename to tests/image/test_regionCentroid.m index c7da221..1c08dfa 100644 --- a/tests/image/test_regionCentroids.m +++ b/tests/image/test_regionCentroid.m @@ -1,13 +1,13 @@ -function tests = test_regionCentroids -% Test suite for the file regionCentroids. +function tests = test_regionCentroid +% Test suite for the file regionCentroid. % -% Test suite for the file regionCentroids +% Test suite for the file regionCentroid % % Example -% test_regionCentroids +% test_regionCentroid % % See also -% regionCentroids +% regionCentroid % ------ % Author: David Legland @@ -27,7 +27,7 @@ function test_Simple(testCase) %#ok<*DEFNU> data(6:10, 6:10) = 8; img = Image('Data', data, 'Type', 'Label'); -[centroids, labels] = regionCentroids(img); +[centroids, labels] = regionCentroid(img); assertEqual(testCase, size(centroids), [4 2]); assertEqual(testCase, centroids, [3 3;8 3;3 8;8 8]); diff --git a/tests/image/test_regionElementCounts.m b/tests/image/test_regionElementCount.m similarity index 79% rename from tests/image/test_regionElementCounts.m rename to tests/image/test_regionElementCount.m index 64d6006..a0d7226 100644 --- a/tests/image/test_regionElementCounts.m +++ b/tests/image/test_regionElementCount.m @@ -1,13 +1,13 @@ -function tests = test_regionElementCounts -% Test suite for the file regionElementCounts. +function tests = test_regionElementCount +% Test suite for the file regionElementCount. % -% Test suite for the file regionElementCounts +% Test suite for the file regionElementCount % % Example -% test_regionElementCounts +% test_regionElementCount % % See also -% regionElementCounts +% regionElementCount % ------ % Author: David Legland @@ -27,7 +27,7 @@ function test_2d(testCase) %#ok<*DEFNU> data(4:8, 4:8) = 9; img = Image('Data', data, 'Type', 'label'); -counts = regionElementCounts(img); +counts = regionElementCount(img); assertEqual(testCase, length(counts), 4); assertEqual(testCase, counts, [1 5 5 25]'); @@ -47,7 +47,7 @@ function test_3d(testCase) %#ok<*DEFNU> data(4:8, 4:8, 4:8) = 19; img = Image('Data', data, 'Type', 'label'); -counts = regionElementCounts(img); +counts = regionElementCount(img); assertEqual(testCase, length(counts), 8); assertEqual(testCase, counts, [1 5 5 5 25 25 25 125]'); diff --git a/tests/image/test_regionEquivalentEllipses.m b/tests/image/test_regionEquivalentEllipse.m similarity index 71% rename from tests/image/test_regionEquivalentEllipses.m rename to tests/image/test_regionEquivalentEllipse.m index 9798a48..2fe6bd4 100644 --- a/tests/image/test_regionEquivalentEllipses.m +++ b/tests/image/test_regionEquivalentEllipse.m @@ -1,13 +1,13 @@ -function tests = test_regionEquivalentEllipses -% Test suite for the file regionEquivalentEllipses. +function tests = test_regionEquivalentEllipse +% Test suite for the file regionEquivalentEllipse. % -% Test suite for the file regionEquivalentEllipses +% Test suite for the file regionEquivalentEllipse % % Example -% test_regionEquivalentEllipses +% test_regionEquivalentEllipse % % See also -% regionEquivalentEllipses +% regionEquivalentEllipse % ------ % Author: David Legland @@ -22,7 +22,7 @@ function test_Simple(testCase) %#ok<*DEFNU> img = Image.read('circles.png'); -elli = regionEquivalentEllipses(img); +elli = regionEquivalentEllipse(img > 0); assertEqual(testCase, size(elli), [1 5]); @@ -37,7 +37,7 @@ function test_severalLabels(testCase) %#ok<*DEFNU> data(6:10, 6:10) = 8; img = Image('Data', data, 'Type', 'Label'); -[elli, labels] = regionEquivalentEllipses(img); +[elli, labels] = regionEquivalentEllipse(img); assertEqual(testCase, size(elli), [4 5]); assertEqual(testCase, size(labels), [4 1]); diff --git a/tests/image/test_regionEquivalentEllipsoid.m b/tests/image/test_regionEquivalentEllipsoid.m new file mode 100644 index 0000000..c5c56c6 --- /dev/null +++ b/tests/image/test_regionEquivalentEllipsoid.m @@ -0,0 +1,49 @@ +function tests = test_regionEquivalentEllipsoid +% Test suite for the file regionEquivalentEllipsoid. +% +% Test suite for the file regionEquivalentEllipsoid +% +% Example +% test_regionEquivalentEllipsoid +% +% See also +% regionEquivalentEllipsoid + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-03, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_Simple(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +fileName = 'ellipsoid_Center30x27x25_Size20x12x8_Orient40x30x20.tif'; +img = Image.read(fullfile('files', fileName)); + +elli = regionEquivalentEllipsoid(img > 0); + +assertEqual(testCase, size(elli), [1 9]); +assertEqual(testCase, [30 27 25], elli(1:3), 'AbsTol', 0.5); +assertEqual(testCase, [20 12 8], elli(4:6), 'AbsTol', 0.5); +assertEqual(testCase, [40 30 20], elli(7:9), 'AbsTol', 0.5); + + +function test_Calibrated(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +fileName = 'ellipsoid_Center30x27x25_Size20x12x8_Orient40x30x20.tif'; +img = Image.read(fullfile('files', fileName)); +img.Spacing = [0.5 0.5 0.5]; +img.Origin = [0.5 0.5 0.5]; + +elli = regionEquivalentEllipsoid(img > 0); + +assertEqual(testCase, size(elli), [1 9]); +assertEqual(testCase, [15 13.5 12.5], elli(1:3), 'AbsTol', 0.5); +assertEqual(testCase, [10 6 4], elli(4:6), 'AbsTol', 0.5); +assertEqual(testCase, [40 30 20], elli(7:9), 'AbsTol', 0.5); + + diff --git a/tests/image/test_regionEulerNumber.m b/tests/image/test_regionEulerNumber.m new file mode 100644 index 0000000..b8ea1b1 --- /dev/null +++ b/tests/image/test_regionEulerNumber.m @@ -0,0 +1,192 @@ +function tests = test_regionEulerNumber +% Test suite for the file regionEulerNumber. +% +% Test suite for the file regionEulerNumber +% +% Example +% test_regionEulerNumber +% +% See also +% regionEulerNumber + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + + +function test_2d_SimplePoints(testCase) +% Four points in a black image. + +img = Image.false([10 10]); +img(3, 4) = true; +img(7, 8) = true; +img(1, 2) = true; +img(10, 2) = true; + +epc = regionEulerNumber(img); + +assertEqual(testCase, 4, epc); + + +function test_2d_BorderPoints(testCase) +% Five points in a black image, on the boundary. + +img = Image.false([10 10]); +img(1, 2) = true; +img(10, 2) = true; +img(6, 10) = true; +img(4, 10) = true; +img(10, 10) = true; + +epc = regionEulerNumber(img); + +assertEqual(testCase, 5, epc); + + +function test_2d_Conn8(testCase) +% test with 3 points touching by corner. + +img = Image.false([10 10]); +img(3, 4) = true; +img(4, 5) = true; +img(5, 4) = true; + +epc4 = regionEulerNumber(img); +epc8 = regionEulerNumber(img, 8); + +assertEqual(testCase, 3, epc4); +assertEqual(testCase, 1, epc8); + + +function test_2d_Labels(testCase) +% Label images with several regions return vector of results. + +% create a label image with 3 labels, one of them with a hole +img = Image(zeros([10 10]), 'Type', 'Label'); +img(2:3, 2:3) = 3; +img(6:8, 2:3) = 5; +img(3:5, 5:8) = 9; +img(4, 6) = 0; + +[chi, labels] = regionEulerNumber(img); + +assertEqual(testCase, chi, [1 1 0]'); +assertEqual(testCase, labels, [3 5 9]'); + + + +function test_3d_ball_C6(testCase) + +% create a simple ball +img = Image.false([5 5 5]); +img(2:4, 2:4, 2:4) = true; + +% check EPC=1 +epcTh = 1; +assertEqual(testCase, epcTh, regionEulerNumber(img, 6)); + +% add a hole in the ball -> EPC=2 +img(3, 3, 3) = false; + +% check EPC=2 +epcTh = 2; +assertEqual(testCase, epcTh, regionEulerNumber(img, 6)); + + +function test_3d_ball_C26(testCase) + +% create a simple ball +img = Image.false([5 5 5]); +img(2:4, 2:4, 2:4) = true; + +% check EPC=1 +epcTh = 1; +assertEqual(testCase, epcTh, regionEulerNumber(img, 26)); + +% add a hole in the ball -> EPC=2 +img(3, 3, 3) = false; + +% check EPC=2 +epcTh = 2; +assertEqual(testCase, epcTh, regionEulerNumber(img, 26)); + + + +function test_3d_torus_C6(testCase) + +% create a simple ball +img = Image.false([5 5 5]); +img(2:4, 2:4, 2:4) = true; +img(3, 3, 2:4) = false; + +% check EPC=0 +epcTh = 0; +assertEqual(testCase, epcTh, regionEulerNumber(img, 6)); + + +function test_3d_torus_C26(testCase) + +% create a simple ball +img = Image.false([5 5 5]); +img(2:4, 2:4, 2:4) = true; +img(3, 3, 2:4) = false; + +% check EPC=0 +epcTh = 0; +assertEqual(testCase, epcTh, regionEulerNumber(img, 26)); + + +function test_3D_cubeDiagonals_C6(testCase) + +% create a small 3D image with points along cube diagonal +img = Image.false([5 5 5]); +for i = 1:5 + img(i, i, i) = true; + img(6-i, i, i) = true; + img(i, 6-i, i) = true; + img(6-i, 6-i, i) = true; +end + +epc6 = regionEulerNumber(img, 6); + +epc6Th = 17; +assertEqual(testCase, epc6, epc6Th); + + +function test_3D_cubeDiagonals_C26(testCase) + +% create a small 3D image with points along cube diagonal +img = Image.false([7 7 7]); +for i = 2:6 + img(i, i, i) = true; + img(8-i, i, i) = true; + img(i, 8-i, i) = true; + img(8-i, 8-i, i) = true; +end + +epc26 = regionEulerNumber(img, 26); + +epc26Th = 1; +assertEqual(testCase, epc26, epc26Th); + + +function test_3D_cubeDiagonals_touchingBorder_C26(testCase) + +% create a small 3D image with points along cube diagonal +img = Image.false([5 5 5]); +for i = 1:5 + img(i, i, i) = true; + img(6-i, i, i) = true; + img(i, 6-i, i) = true; + img(6-i, 6-i, i) = true; +end + +epc26 = regionEulerNumber(img, 26); + +epc26Th = 1; +assertEqual(testCase, epc26, epc26Th); + diff --git a/tests/image/test_regionGeodesicDiameter.m b/tests/image/test_regionGeodesicDiameter.m new file mode 100644 index 0000000..3601790 --- /dev/null +++ b/tests/image/test_regionGeodesicDiameter.m @@ -0,0 +1,194 @@ +function tests = test_regionGeodesicDiameter +% Test suite for the file regionGeodesicDiameter. +% +% Test suite for the file regionGeodesicDiameter +% +% Example +% test_regionGeodesicDiameter +% +% See also +% regionGeodesicDiameter + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-18, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + + +% function test_Simple(testCase) %#ok<*DEFNU> +% % Test call of function without argument. +% regionGeodesicDiameter(); +% value = 10; +% assertEqual(testCase, value, 10); + +function test_Square5x5(testCase) %#ok<*DEFNU> + +img = Image.false(8, 8); +img(2:6, 3:7) = 1; + +assertEqual(testCase, (2*11+2*5+1)/5, regionGeodesicDiameter(img)); +assertEqual(testCase, 5, regionGeodesicDiameter(img, [1 1])); +assertEqual(testCase, 9, regionGeodesicDiameter(img, [1 2])); +assertEqual(testCase, 19/3, regionGeodesicDiameter(img, [3 4])); + + +function test_SmallSpiral(testCase) %#ok<*DEFNU> + +data = [... + 0 0 0 0 0 0 0 0 0 0; ... + 0 0 0 0 0 0 0 0 0 0; ... + 0 1 1 1 1 1 1 1 1 0; ... + 0 0 0 0 0 0 0 1 1 0; ... + 0 0 1 1 1 1 0 0 1 0; ... + 0 1 0 0 0 1 0 0 1 0; ... + 0 1 1 0 0 0 0 1 1 0; ... + 0 0 1 1 1 1 1 1 1 0; ... + 0 0 0 0 1 1 0 0 0 0; ... + 0 0 0 0 0 0 0 0 0 0]; + +% number of orthogonal and diagonal move between extremities +no = 5 + 1 + 3 + 2; +nd = 2 + 2 + 3 + 1; + +img = Image(data, 'type', 'binary'); + +exp11 = no + nd + 1; +assertEqual(testCase, exp11, regionGeodesicDiameter(img, [1 1])); +exp12 = no + nd*2 + 1; +assertEqual(testCase, exp12, regionGeodesicDiameter(img, [1 2])); +exp34 = (no*3 + nd*4)/3 + 1; +assertEqual(testCase, exp34, regionGeodesicDiameter(img, [3 4])); + + + +function test_VerticalLozenge(testCase) +% vertical lozenge that did not pass test with first version of algo + +img = Image([... + 0 0 0 0 0 0 0 ; ... + 0 0 0 1 0 0 0 ; ... + 0 0 1 1 1 0 0 ; ... + 0 0 1 1 1 0 0 ; ... + 0 1 1 1 1 1 0 ; ... + 0 0 1 1 1 0 0 ; ... + 0 0 1 1 1 0 0 ; ... + 0 0 0 1 0 0 0 ; ... + 0 0 0 0 0 0 0 ; ... + ], 'type', 'binary'); +exp = 7; + +assertEqual(testCase, exp, regionGeodesicDiameter(img)); +assertEqual(testCase, exp, regionGeodesicDiameter(img, [1 1])); +assertEqual(testCase, exp, regionGeodesicDiameter(img, [1 2])); +assertEqual(testCase, exp, regionGeodesicDiameter(img, [3 4])); +assertEqual(testCase, uint16(exp), regionGeodesicDiameter(img, uint16([3 4]))); + + +function test_SeveralParticles(testCase) + +img = Image(zeros([10 10]), 'type', 'label'); +img(2:4, 2:4) = 1; +img(6:9, 2:4) = 2; +img(2:4, 6:9) = 3; +img(6:9, 6:9) = 4; + +exp11 = [2 3 3 3]' + 1; +exp12 = [4 5 5 6]' + 1; +exp34 = [8/3 11/3 11/3 12/3]' + 1; + +% test on label image +assertEqual(testCase, exp11, regionGeodesicDiameter(img, [1 1])); +assertEqual(testCase, exp12, regionGeodesicDiameter(img, [1 2])); +assertEqual(testCase, exp34, regionGeodesicDiameter(img, [3 4])); + + +function test_SeveralParticles_UInt16(testCase) + +img = Image(zeros([10 10]), 'type', 'label'); +img(2:4, 2:4) = 1; +img(6:9, 2:4) = 2; +img(2:4, 6:9) = 3; +img(6:9, 6:9) = 4; + +exp11 = uint16([2 3 3 3]' + 1); +exp12 = uint16([4 5 5 6]' + 1); +exp34 = uint16([8/3 11/3 11/3 12/3]' + 1); + +% test on label image +assertEqual(testCase, exp11, regionGeodesicDiameter(img, uint16([1 1]))); +assertEqual(testCase, exp12, regionGeodesicDiameter(img, uint16([1 2]))); +assertEqual(testCase, exp34, regionGeodesicDiameter(img, uint16([3 4]))); + + +function test_TouchingRegions(testCase) + +img = Image([... + 0 0 0 0 0 0 0 0; ... + 0 1 1 2 2 3 3 0; ... + 0 1 1 2 2 3 3 0; ... + 0 1 1 4 4 3 3 0; ... + 0 1 1 4 4 3 3 0; ... + 0 1 1 5 5 3 3 0; ... + 0 1 1 5 5 3 3 0; ... + 0 0 0 0 0 0 0 0; ... +], 'type', 'label'); + +exp11 = [5 1 5 1 1]' + 1; +exp12 = [6 2 6 2 2]' + 1; +exp34 = [16 4 16 4 4]'/3 + 1; + +assertEqual(testCase, exp11, regionGeodesicDiameter(img, [1 1])); +assertEqual(testCase, exp12, regionGeodesicDiameter(img, [1 2])); +assertEqual(testCase, exp34, regionGeodesicDiameter(img, [3 4])); + + +function test_MissingLabels(testCase) + +img = Image([... + 0 0 0 0 0 0 0 0 0 0; ... + 0 1 1 0 2 2 0 3 3 0; ... + 0 1 1 0 2 2 0 3 3 0; ... + 0 1 1 0 0 0 0 3 3 0; ... + 0 1 1 0 7 7 0 3 3 0; ... + 0 1 1 0 7 7 0 3 3 0; ... + 0 1 1 0 0 0 0 3 3 0; ... + 0 1 1 0 9 9 0 3 3 0; ... + 0 1 1 0 9 9 0 3 3 0; ... + 0 0 0 0 0 0 0 0 0 0; ... +], 'Type', 'Label'); + +exp11 = [7 1 7 1 1]' + 1; +exp12 = [8 2 8 2 2]' + 1; +exp34 = [22 4 22 4 4]'/3 + 1; + +% test on label image +assertEqual(testCase, exp11, regionGeodesicDiameter(img, [1 1])); +assertEqual(testCase, exp12, regionGeodesicDiameter(img, [1 2])); +assertEqual(testCase, exp34, regionGeodesicDiameter(img, [3 4])); + + +function test_OutputLabels(testCase) + +img = Image([... + 0 0 0 0 0 0 0 0 0 0; ... + 0 1 1 0 2 2 0 3 3 0; ... + 0 1 1 0 2 2 0 3 3 0; ... + 0 1 1 0 0 0 0 3 3 0; ... + 0 1 1 0 7 7 0 3 3 0; ... + 0 1 1 0 7 7 0 3 3 0; ... + 0 1 1 0 0 0 0 3 3 0; ... + 0 1 1 0 9 9 0 3 3 0; ... + 0 1 1 0 9 9 0 3 3 0; ... + 0 0 0 0 0 0 0 0 0 0; ... +], 'type', 'label'); + +exp1 = [22 4 22 4 4]'/3 + 1; +exp2 = [1 2 3 7 9]'; + +% test on label image +[res, labels] = regionGeodesicDiameter(img, [3 4]); +assertEqual(testCase, exp1, res); +assertEqual(testCase, exp2, labels); diff --git a/tests/image/test_regionInscribedCircle.m b/tests/image/test_regionInscribedCircle.m new file mode 100644 index 0000000..7aa0dc6 --- /dev/null +++ b/tests/image/test_regionInscribedCircle.m @@ -0,0 +1,83 @@ +function tests = test_regionInscribedCircle +% Test suite for the file regionInscribedCircle. +% +% Test suite for the file regionInscribedCircle +% +% Example +% test_regionInscribedCircle +% +% See also +% regionInscribedCircle + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-19, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_Simple(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +img = Image.false(100, 100); +img(30:60, 20:80) = 1; + +circle = regionInscribedCircle(img); + +assertEqual(testCase, size(circle), [1 3]); +assertEqual(testCase, circle(3), 16, 'AbsTol', 0.01); + + +function test_MultiLabels(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +img = Image([ ... + 0 0 0 0 0 0 0 0 0 0 0; ... + 0 1 1 1 0 4 4 4 4 4 0; ... + 0 1 1 1 0 4 4 4 4 4 0; ... + 0 1 1 1 0 4 4 4 4 4 0; ... + 0 0 0 0 0 0 0 0 0 0 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 6 6 6 6 6 6 6 6 6 0; ... + 0 0 0 0 0 0 0 0 0 0 0], 'Type', 'Label'); + +[circles, labels] = regionInscribedCircle(img); + +assertEqual(testCase, size(circles), [3 3]); +assertEqual(testCase, length(labels), 3); + +expRadius = [2 2 5]'; +assertEqual(testCase, circles(:,3), expRadius, 'AbsTol', 0.01); + + +function test_LabelsTouchingImageBorder(testCase) %#ok<*DEFNU> +% Test call of function without argument. + +img = Image([ ... + 1 1 1 0 4 4 4 4 4; ... + 1 1 1 0 4 4 4 4 4; ... + 1 1 1 0 4 4 4 4 4; ... + 0 0 0 0 0 0 0 0 0; ... + 6 6 6 6 6 6 6 6 6; ... + 6 6 6 6 6 6 6 6 6; ... + 6 6 6 6 6 6 6 6 6; ... + 6 6 6 6 6 6 6 6 6; ... + 6 6 6 6 6 6 6 6 6], 'Type', 'Label'); + +[circles, labels] = regionInscribedCircle(img); + +assertEqual(testCase, size(circles), [3 3]); +assertEqual(testCase, length(labels), 3); + +expRadius = [3 3 5]'; +assertEqual(testCase, circles(:,3), expRadius, 'AbsTol', 0.01); + diff --git a/tests/image/test_regionMeanBreadth.m b/tests/image/test_regionMeanBreadth.m new file mode 100644 index 0000000..eb8b13f --- /dev/null +++ b/tests/image/test_regionMeanBreadth.m @@ -0,0 +1,125 @@ +function tests = test_regionMeanBreadth +% Test suite for the file regionMeanBreadth. +% +% Test suite for the file regionMeanBreadth +% +% Example +% test_regionMeanBreadth +% +% See also +% regionMeanBreadth + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_ball_D3(testCase) + +[x, y, z] = meshgrid(1:30, 1:30, 1:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); + +mb = regionMeanBreadth(img, 3); + +mbth = 2 * 10; +assertTrue(testCase, abs(mb - mbth) < mbth * 0.05); + + +function test_ball_D13(testCase) + +[x, y, z] = meshgrid(1:30, 1:30, 1:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); + +mb = regionMeanBreadth(img, 13); + +mbth = 2 * 10; +assertTrue(testCase, abs(mb - mbth) < mbth * 0.05); + + +function test_ball_D3_aniso(testCase) + +[x, y, z] = meshgrid(1:30, 1:2:30, 1:3:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); +img.Spacing = [1 2 3]; + +mb = regionMeanBreadth(img, 3); + +mbth = 2 * 10; +assertTrue(testCase, abs(mb - mbth) < mbth * 0.05); + + +function test_ball_D13_aniso(testCase) + +[x, y, z] = meshgrid(1:30, 1:2:30, 1:3:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); +img.Spacing = [1 2 3]; + +mb = regionMeanBreadth(img, 13); + +mbth = 2 * 10; +assertTrue(testCase, abs(mb - mbth) < mbth * 0.05); + + +function test_TouchingBorder_D3(testCase) + +img1 = Image.false([7 7 7]); +img1(2:6, 2:6, 2:6) = true; +img2 = Image.true([5 5 5]); + +mb1 = regionMeanBreadth(img1, 3); +mb2 = regionMeanBreadth(img2, 3); + +assertEqual(testCase, mb1, mb2); + + +function test_TouchingBorder_D13(testCase) + +img1 = Image.false([7 7 7]); +img1(2:6, 2:6, 2:6) = true; +img2 = Image.true([5 5 5]); + +mb1 = regionMeanBreadth(img1, 13); +mb2 = regionMeanBreadth(img2, 13); + +assertEqual(testCase, mb1, mb2); + + + +function test_Labels(testCase) + +img = Image(zeros([6 6 6]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 2; +img(4:6, 1:3, 1:3) = 3; +img(1:3, 4:6, 1:3) = 5; +img(4:6, 4:6, 1:3) = 7; +img(1:3, 1:3, 4:6) = 11; +img(4:6, 1:3, 4:6) = 13; +img(1:3, 4:6, 4:6) = 17; +img(4:6, 4:6, 4:6) = 19; + +[mb, labels] = regionMeanBreadth(img); +assertEqual(testCase, size(mb), [8 1], .01); +assertEqual(testCase, labels, [2 3 5 7 11 13 17 19]'); + + +function test_LabelSelection(testCase) + +img = Image(zeros([6 6 6]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 2; +img(4:6, 1:3, 1:3) = 3; +img(1:3, 4:6, 1:3) = 5; +img(4:6, 4:6, 1:3) = 7; +img(1:3, 1:3, 4:6) = 11; +img(4:6, 1:3, 4:6) = 13; +img(1:3, 4:6, 4:6) = 17; +img(4:6, 4:6, 4:6) = 19; +labels = [2 3 5 7 11 13 17 19]'; + +mb = regionMeanBreadth(img, labels); +assertEqual(testCase, size(mb), [8 1]); + +mb2 = regionMeanBreadth(img, labels(1:2:end)); +assertEqual(testCase, size(mb2), [4 1]); diff --git a/tests/image/test_regionPerimeter.m b/tests/image/test_regionPerimeter.m new file mode 100644 index 0000000..b2d9e0b --- /dev/null +++ b/tests/image/test_regionPerimeter.m @@ -0,0 +1,121 @@ +function tests = test_regionPerimeter +% Test suite for the file regionPerimeter. +% +% Test suite for the file regionPerimeter +% +% Example +% test_regionPerimeter +% +% See also +% regionPerimeter + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_diskR10_D2(testCase) + +lx = 1:30; +[x, y] = meshgrid(lx, lx); +img = Image(hypot(x - 15.12, y - 15.23) < 10); + +p = regionPerimeter(img, 2); + +% assert perimeter estimate is within 5% of actual value +pTh = 2 * pi * 10; +assertTrue(testCase, abs(p - pTh) < 3); + + +function test_diskR10_D4(testCase) + +lx = 1:30; +[x, y] = meshgrid(lx, lx); +img = Image(hypot(x - 15.12, y - 15.23) < 10); + +p = regionPerimeter(img, 4); + +% assert perimeter estimate is within 5% of actual value +pTh = 2 * pi * 10; +assertTrue(testCase, abs(p - pTh) < 3); + + +function test_touchingBorder_D2(testCase) + +img1 = Image.false([7, 7]); +img1(2:6, 2:6) = 1; +img2 = Image.true([5, 5]); + +nd = 2; +p = regionPerimeter(img1, nd); +pb = regionPerimeter(img2, nd); + +assertEqual(testCase, p, pb); + + +function test_touchingBorder_D4(testCase) + +img1 = Image.false([7, 7]); +img1(2:6, 2:6) = 1; +img2 = Image.true([5, 5]); + +nd = 4; +p = regionPerimeter(img1, nd); +pb = regionPerimeter(img2, nd); + +assertEqual(testCase, p, pb); + + +function test_diskR10_D2_Aniso(testCase) + +[x, y] = meshgrid(1:2:50, 1:3:50); +img = Image(hypot(x - 25.12, y - 25.23) < 10); +img.Spacing = [2 3]; + +p = regionPerimeter(img, 2); + +% assert perimeter estimate is within 5% of actual value +pTh = 2 * pi * 10; +assertTrue(testCase, abs(p - pTh) < 3); + + +function test_diskR10_D4_Aniso(testCase) + +[x, y] = meshgrid(1:2:50, 1:3:50); +img = Image(hypot(x - 25.12, y - 25.23) < 10); +img.Spacing = [2 3]; + +p = regionPerimeter(img, 4); + +% assert perimeter estimate is within 5% of actual value +pTh = 2 * pi * 10; +assertTrue(testCase, abs(p - pTh) < 3); + + +function test_touchingBorder_D4_Aniso(testCase) + +img1 = Image.false([7, 7]); +img1(2:6, 2:6) = 1; +img2 = Image.true([5, 5]); + +nd = 4; +p = regionPerimeter(img1, nd, [2 3]); +pb = regionPerimeter(img2, nd, [2 3]); + +assertEqual(testCase, p, pb); + + +function testLabelImage(testCase) + +img = Image.read('coins.png'); +lbl = componentLabeling(img > 100); + +p = regionPerimeter(lbl); + +assertEqual(testCase, 10, length(p)); +assertTrue(min(p) > 150); +assertTrue(max(p) < 300); + diff --git a/tests/image/test_regionSurfaceArea.m b/tests/image/test_regionSurfaceArea.m new file mode 100644 index 0000000..1f1e1db --- /dev/null +++ b/tests/image/test_regionSurfaceArea.m @@ -0,0 +1,157 @@ +function tests = test_regionSurfaceArea +% Test suite for the file regionSurfaceArea. +% +% Test suite for the file regionSurfaceArea +% +% Example +% test_regionSurfaceArea +% +% See also +% regionSurfaceArea + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + +function test_ball_D3(testCase) + +[x, y, z] = meshgrid(1:30, 1:30, 1:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); + +s = regionSurfaceArea(img, 3); + +sth = 4 * pi * 10^2; +assertTrue(testCase, abs(s - sth) < sth * 0.05); + + +function test_ball_D13(testCase) + +[x, y, z] = meshgrid(1:30, 1:30, 1:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); + +s = regionSurfaceArea(img, 13); + +sth = 4 * pi * 10^2; +assertTrue(testCase, abs(s - sth) < sth * 0.05); + + +function test_ball_D3_aniso(testCase) + +[x, y, z] = meshgrid(1:30, 1:2:30, 1:3:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); +img.Spacing = [1 2 3]; + +s = regionSurfaceArea(img, 3); + +sth = 4 * pi * 10^2; +assertTrue(testCase, abs(s - sth) < sth * 0.05); + + +function test_ball_D13_aniso(testCase) + +[x, y, z] = meshgrid(1:30, 1:2:30, 1:3:30); +img = Image(sqrt((x-15.12).^2 + (y-15.23).^2 + (z-15.34).^2) < 10); +img.Spacing = [1 2 3]; + +s = regionSurfaceArea(img, 13); + +sth = 4 * pi * 10^2; +assertTrue(testCase, abs(s - sth) < sth * 0.05); + + +function test_TouchingBorder_D3(testCase) + +img1 = Image.false([7 7 7]); +img1(2:6, 2:6, 2:6) = true; +img2 = Image.true([5 5 5]); + +s1 = regionSurfaceArea(img1, 3); +s2 = regionSurfaceArea(img2, 3); + +assertEqual(testCase, s1, s2); + + +function test_TouchingBorder_D13(testCase) + +img1 = Image.false([7 7 7]); +img1(2:6, 2:6, 2:6) = true; +img2 = Image.true([5 5 5]); + +s1 = regionSurfaceArea(img1, 13); +s2 = regionSurfaceArea(img2, 13); + +assertEqual(testCase, s1, s2); + + +function test_Labels_D3(testCase) + +img = Image(zeros([5 5 5]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 1; +img(4:5, 1:3, 1:3) = 2; +img(1:3, 4:5, 1:3) = 3; +img(4:5, 4:5, 1:3) = 4; +img(1:3, 1:3, 4:5) = 5; +img(4:5, 1:3, 4:5) = 6; +img(1:3, 4:5, 4:5) = 7; +img(4:5, 4:5, 4:5) = 8; + +s3 = regionSurfaceArea(img, 3); +assertEqual(testCase, 8, length(s3)); + + +function test_Labels_D13(testCase) + +img = Image(zeros([5 5 5]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 1; +img(4:5, 1:3, 1:3) = 2; +img(1:3, 4:5, 1:3) = 3; +img(4:5, 4:5, 1:3) = 4; +img(1:3, 1:3, 4:5) = 5; +img(4:5, 1:3, 4:5) = 6; +img(1:3, 4:5, 4:5) = 7; +img(4:5, 4:5, 4:5) = 8; + +s13 = regionSurfaceArea(img, 13); +assertEqual(testCase, 8, length(s13)); + + +function test_LabelSelection_D3(testCase) + +img = Image(zeros([5 5 5]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 2; +img(4:5, 1:3, 1:3) = 3; +img(1:3, 4:5, 1:3) = 5; +img(4:5, 4:5, 1:3) = 7; +img(1:3, 1:3, 4:5) = 11; +img(4:5, 1:3, 4:5) = 13; +img(1:3, 4:5, 4:5) = 17; +img(4:5, 4:5, 4:5) = 19; + +[b3, labels3] = regionSurfaceArea(img, 3); + +assertEqual(testCase, 8, length(b3)); +assertEqual(testCase, 8, length(labels3)); +assertEqual(testCase, labels3, [2 3 5 7 11 13 17 19]'); + + +function test_LabelSelection_D13(testCase) + +img = Image(zeros([5 5 5]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 2; +img(4:5, 1:3, 1:3) = 3; +img(1:3, 4:5, 1:3) = 5; +img(4:5, 4:5, 1:3) = 7; +img(1:3, 1:3, 4:5) = 11; +img(4:5, 1:3, 4:5) = 13; +img(1:3, 4:5, 4:5) = 17; +img(4:5, 4:5, 4:5) = 19; + +[s13, labels13] = regionSurfaceArea(img, 13); + +assertEqual(testCase, 8, length(s13)); +assertEqual(testCase, 8, length(labels13)); +assertEqual(testCase, labels13, [2 3 5 7 11 13 17 19]'); diff --git a/tests/image/test_regionVolume.m b/tests/image/test_regionVolume.m new file mode 100644 index 0000000..77a06fe --- /dev/null +++ b/tests/image/test_regionVolume.m @@ -0,0 +1,98 @@ +function tests = test_regionVolume +% Test suite for the file regionVolume. +% +% Test suite for the file regionVolume +% +% Example +% test_regionVolume +% +% See also +% regionVolume + +% ------ +% Author: David Legland +% e-mail: david.legland@inrae.fr +% Created: 2021-11-02, using Matlab 9.10.0.1684407 (R2021a) Update 3 +% Copyright 2021 INRAE - BIA-BIBS. + +tests = functiontests(localfunctions); + + +function testBasic(testCase) + +img = Image.false([4 5 6]); +img(2:end-1, 2:end-1, 2:end-1) = true; + +v = regionVolume(img); + +exp = prod([2 3 4]); +assertEqual(testCase, exp, v); + + +function testAddBorder(testCase) + +img1 = Image.false([7 7 7]); +img1(2:6, 2:6, 2:6) = true; +img2 = Image.true([5 5 5]); + +v1 = regionVolume(img1); +v2 = regionVolume(img2); + +assertEqual(testCase, v1, v2); + + +function test_Labels(testCase) + +img = Image(zeros([6 6 6]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 2; +img(4:6, 1:3, 1:3) = 3; +img(1:3, 4:6, 1:3) = 5; +img(4:6, 4:6, 1:3) = 7; +img(1:3, 1:3, 4:6) = 11; +img(4:6, 1:3, 4:6) = 13; +img(1:3, 4:6, 4:6) = 17; +img(4:6, 4:6, 4:6) = 19; + +[vols, labels] = regionVolume(img); +assertEqual(testCase, vols, repmat(27, 8, 1), .01); +assertEqual(testCase, labels, [2 3 5 7 11 13 17 19]'); + + +function test_SelectedLabels(testCase) +% Compute volume on a selection of regions within a label image. + +img = Image(zeros([6 6 6]), 'type', 'label'); +img(1:3, 1:3, 1:3) = 2; +img(4:6, 1:3, 1:3) = 3; +img(1:3, 4:6, 1:3) = 5; +img(4:6, 4:6, 1:3) = 7; +img(1:3, 1:3, 4:6) = 11; +img(4:6, 1:3, 4:6) = 13; +img(1:3, 4:6, 4:6) = 17; +img(4:6, 4:6, 4:6) = 19; +labels = [2 3 5 7 11 13 17 19]'; + +vols = regionVolume(img, labels); +assertEqual(testCase, size(vols), [8 1]); +assertEqual(testCase, vols, repmat(27, 8, 1), .01); + +vols2 = regionVolume(img, labels(1:2:end)); +assertEqual(testCase, size(vols2), [4 1]); +assertEqual(testCase, vols2, repmat(27, 4, 1), .01); + + +function test_Anisotropic(testCase) + +img1 = Image.false([7 7 7]); +img1(2:6, 2:6, 2:6) = true; +img2 = Image.true([5 5 5]); +img1.Spacing = [1 2 3]; +img2.Spacing = [1 2 3]; + +v1 = regionVolume(img1); +v2 = regionVolume(img2); + +expectedVolume = (5*5*5) * (1*2*3); % number of elements times voxel volume +assertEqual(testCase, v1, expectedVolume); +assertEqual(testCase, v2, expectedVolume); + diff --git a/tests/image/test_std.m b/tests/image/test_std.m index f62545b..5863c43 100644 --- a/tests/image/test_std.m +++ b/tests/image/test_std.m @@ -24,7 +24,7 @@ function test_2d(testCase) %#ok<*DEFNU> exp = std(double(dat(:))); res = std(img); -assertEqual([1 1], size(res)); +assertEqual(testCase, [1 1], size(res)); assertEqual(testCase, exp, res, 'AbsTol', 1e-10); function test_3d(testCase) @@ -34,6 +34,6 @@ function test_3d(testCase) exp = std(double(dat(:))); res = std(img); -assertEqual([1 1], size(res)); +assertEqual(testCase, [1 1], size(res)); assertEqual(testCase, exp, res, 'AbsTol', 1e-10); diff --git a/tests/image/test_subsasgn.m b/tests/image/test_subsasgn.m index 013fa49..a9feaa6 100644 --- a/tests/image/test_subsasgn.m +++ b/tests/image/test_subsasgn.m @@ -27,11 +27,11 @@ function test_subsasgn_2d(testCase) %#ok<*DEFNU> % set one element img(1) = 99; -assertEqual(99, img(1)); +assertEqual(testCase, 99, img(1)); img(2) = 99; -assertEqual(99, img(2)); +assertEqual(testCase, 99, img(2)); img(end) = 99; -assertEqual(99, img(end)); +assertEqual(testCase, 99, img(end)); % set one row (y = cte) new = 8:2:18; diff --git a/tests/image/test_var.m b/tests/image/test_var.m index e087984..4f656e3 100644 --- a/tests/image/test_var.m +++ b/tests/image/test_var.m @@ -24,7 +24,7 @@ function test_2d(testCase) %#ok<*DEFNU> exp = var(double(dat(:))); res = var(img); -assertEqual([1 1], size(res)); +assertEqual(testCase, [1 1], size(res)); assertEqual(testCase, exp, res, 'AbsTol', 1e-10); function test_3d(testCase) @@ -34,7 +34,7 @@ function test_3d(testCase) exp = var(double(dat(:))); res = var(img); -assertEqual([1 1], size(res)); +assertEqual(testCase, [1 1], size(res)); assertEqual(testCase, exp, res, 'AbsTol', 1e-10);