Parcourir la source

Merge branch 'release/v1.0.6'

Jérôme BUISINE il y a 5 ans
Parent
commit
4d188d73ba

+ 0 - 0
.nojekyll


+ 9 - 5
README.md

@@ -20,15 +20,15 @@ Modules
 
 This project contains modules.
 
-- **image_processing** : *Image processing module*
+- **processing** : *Image processing module*
     - get_LAB_L_SVD_U(image): *Returns U SVD from L of LAB Image information*
     - get_LAB_L_SVD_s(image): *Returns s (Singular values) SVD from L of LAB Image information*
     - get_LAB_L_SVD_V(image): *Returns V SVD from L of LAB Image information*
     - divide_in_blocks(image, block_size): *Divide image into equal size blocks*
     - rgb_to_mscn(image): *Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN) using only gray level*
-    - rgb_to_grey_low_bits(image, bind=15): *Convert RGB Image into grey image using only 4 low bits values by default*
-    - rgb_to_LAB_L_low_bits(image, bind=15): *Convert RGB Image into LAB L channel image using only 4 low bits values by default*
-    - rgb_to_LAB_L_bits(image, interval): *Convert RGB Image into LAB L channel image using specific interval of bits to keep (2, 5) such as example*
+    - rgb_to_grey_low_bits(image, nb_bits=4): *Convert RGB Image into grey image using only 4 low bits values by default*
+    - rgb_to_LAB_L_low_bits(image, nb_bits=4): *Convert RGB Image into LAB L chanel image using only 4 low bits values by default*
+    - rgb_to_LAB_L_bits(image, interval): *Convert RGB Image into LAB L chanel image using specific interval of bits to keep (2, 5) such as example*
     - normalize_arr(arr): *Normalize array values*
     - normalize_arr_with_range(arr, min, max): *Normalize array values with specific min and max values*
     - normalize_2D_arr(arr): *Return 2D array normalize from its min and max values*
@@ -46,7 +46,11 @@ This project contains modules.
     - get_XYZ_X(image): *Transforms Image into XYZ and returns only 'X' part*
     - get_XYZ_Y(image): *Transforms Image into XYZ and returns only 'Y' part*
     - get_XYZ_Z(image): *Transforms Image into XYZ and returns only 'Z' part*
-    - get_low_bits_img(image, bind=15): *Returns Image or Numpy array with data information reduced using only low bits (by default*
+    - get_low_bits_img(image, nb_bits=4): *Returns Image or Numpy array with data information reduced using only low bits*
+
+- **filters** : *Image filter module*
+    - **noise** : *Noise filters implemented*
+        - white_noise(image, n, distribution_interval=(-0.5, 0.5)) : *Add white noise to image using the **n** variable which manages intensity of noise in interval [1, 999] and the **distribution_interval** variable which manages interval of uniform distribution*
 
 All these modules will be enhanced during development of the project
 

+ 14 - 36
README.rst

@@ -3,6 +3,8 @@ IPFML
 
 Image Processing For Machine Learning package.
 
+This is a package developed during a thesis project.
+
 How to use ?
 ------------
 
@@ -19,39 +21,15 @@ Modules
 
 This project contains modules.
 
-- **image_processing** : *Image processing module*
-    - get_LAB_L_SVD_U(image): *Returns U SVD from L of LAB Image information*
-    - get_LAB_L_SVD_s(image): *Returns s (Singular values) SVD from L of LAB Image information*
-    - get_LAB_L_SVD_V(image): *Returns V SVD from L of LAB Image information*
-    - divide_in_blocks(image, block_size): Divide image into equal size blocks
-    - rgb_to_mscn(image): *Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN) using only gray level*
-    - rgb_to_grey_low_bits(image, bind=15): *Convert RGB Image into grey image using only 4 low bits values by default*
-    - rgb_to_LAB_L_low_bits(image, bind=15): *Convert RGB Image into LAB L channel image using only 4 low bits values by default*
-    - rgb_to_LAB_L_bits(image, interval): *Convert RGB Image into LAB L channel image using specific interval of bits to keep (2, 5) such as example*
-    - normalize_arr(arr): *Normalize array values*
-    - normalize_arr_with_range(arr, min, max): *Normalize array values with specific min and max values*
-    - normalize_2D_arr(arr): *Return 2D array normalize from its min and max values*
-    
-- **metrics** : *Metrics computation of PIL or 2D numpy image*
-    - get_SVD(image): *Transforms PIL Image into SVD*
-    - get_SVD_U(image): *Transforms PIL Image into SVD and returns only 'U' part*
-    - get_SVD_s(image): *Transforms PIL Image into SVD and returns only 's' part*
-    - get_SVD_V(image): *Transforms PIL Image into SVD and returns only 'V' part*
-    - get_LAB(image): *Transforms PIL Image into LAB*
-    - get_LAB_L(image): *Transforms PIL Image into LAB and returns only 'L' part*
-    - get_LAB_A(image): *Transforms PIL Image into LAB and returns only 'A' part*
-    - get_LAB_B(image): *Transforms PIL Image into LAB and returns only 'B' part*
-    - get_XYZ(image): *Transforms PIL Image into XYZ*
-    - get_XYZ_X(image): *Transforms PIL Image into XYZ and returns only 'X' part*
-    - get_XYZ_Y(image): *Transforms PIL Image into XYZ and returns only 'Y' part*
-    - get_XYZ_Z(image): *Transforms PIL Image into XYZ and returns only 'Z' part*
-    - get_low_bits_img(image, bind=15): *Returns Image or Numpy array with data information reduced using only low bits (by default 4)*
-
-All these modules will be enhanced during development of the project
-
-How to contribute
------------------
-
-This git project uses git-flow_ implementation. You are free to contribute to it.
-
-.. _git-flow : https://danielkummer.github.io/git-flow-cheatsheet/
+- **processing** : *Image processing of images*
+- **metrics** : *Metrics computation of PIL or 2D, 3D numpy images*
+- **filters** : *Filters implemented such as noise filters*
+
+All these modules will be enhanced during development of the package.
+
+Documentation
+-------------
+
+For more information about package, documentation_ is available  
+
+.. _documentation: https://gogs.univ-littoral.fr/jerome.buisine/IPFML

+ 22 - 0
docs/Makefile

@@ -0,0 +1,22 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+SOURCEDIR     = source
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+rst:
+	sphinx-apidoc -o ./source ../ipfml/
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

+ 56 - 0
docs/source/_static/pymunk.js

@@ -0,0 +1,56 @@
+$(function (){
+
+    console.log("There!")
+
+    var createList = function(selector){
+
+        var ul = $('<ul>');
+        var selected = $(selector);
+
+        console.log(selected.length);
+        if (selected.length === 0){
+            return;
+        }
+
+        selected.clone().each(function (i,e){
+
+            var p = $(e).children('.descclassname');
+            var n = $(e).children('.descname');
+            var l = $(e).children('.headerlink');
+
+            var a = $('<a>');
+            a.attr('href',l.attr('href')).attr('title', 'Link to this definition');
+
+            a.append(p).append(n);
+
+            var entry = $('<li>').append(a);
+            ul.append(entry);
+        });
+        return ul;
+    }
+
+
+    var c = $('<div style="float:left; min-width: 300px;">');
+
+    var ul0 = c.clone().append($('.submodule-index'))
+
+    customIndex = $('.custom-index');
+    customIndex.empty();
+    customIndex.append(ul0);
+
+    var x = [];
+    x.push(['Classes','dl.class > dt']);
+    x.push(['Functions','dl.function > dt']);
+    x.push(['Variables','dl.data > dt']);
+
+    x.forEach(function (e){
+        var l = createList(e[1]);
+        if (l) {
+            var ul = c.clone()
+                .append('<p class="rubric">'+e[0]+'</p>')
+                .append(l);
+        }
+        customIndex.append(ul);
+    });
+
+});

+ 11 - 0
docs/source/_templates/localtoc.html

@@ -0,0 +1,11 @@
+<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
+{%- if display_toc %}
+
+  {{ toc }}
+{%- endif %}
+
+HERE!!!!
+
+<div class="custom-index container">
+    <script type="text/javascript" src={{ pathto(master_doc)[:-10] + '_static/pymunk.js' }}></script>
+</div>

+ 196 - 0
docs/source/conf.py

@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/master/config
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+sys.path.insert(0, os.path.abspath('../../ipfml'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'IPFML'
+copyright = '2019, Jérôme BUISINE'
+author = 'Jérôme BUISINE'
+
+# The short X.Y version
+version = '0.1.6'
+# The full version, including alpha/beta/rc tags
+release = 'v0.1.6'
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.doctest',
+    'sphinx.ext.coverage',
+    'sphinx.ext.napoleon',
+    'sphinx.ext.autosummary'
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = None
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+html_theme_options = {
+    'canonical_url': '',
+    #'analytics_id': 'UA-XXXXXXX-1',
+    'logo_only': False,
+    'display_version': True,
+    'prev_next_buttons_location': 'bottom',
+    'style_external_links': False,
+    #'vcs_pageview_mode': '',
+    # Toc options
+    'collapse_navigation': True,
+    'sticky_navigation': True,
+    'navigation_depth': 4,
+    'includehidden': True,
+    'titles_only': False
+}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself.  Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'IPFMLdoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'IPFML.tex', 'IPFML Documentation',
+     'Jérôme BUISINE', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'ipfml', 'IPFML Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'IPFML', 'IPFML Documentation',
+     author, 'IPFML', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+
+# -- Options for Epub output -------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+
+# -- Extension configuration -------------------------------------------------

+ 23 - 0
docs/source/description.rst

@@ -0,0 +1,23 @@
+Description
+============
+
+
+Installation
+------------
+
+Just install package using pip 
+
+   >>> pip install ipfml
+
+
+How to use ?
+------------
+
+To use, simply do :
+
+    >>> from PIL import Image
+    >>> from ipfml import image_processing
+    >>> img = Image.open('path/to/image.png')
+    >>> s = image_processing.get_LAB_L_SVD_s(img)
+
+

+ 29 - 0
docs/source/index.rst

@@ -0,0 +1,29 @@
+.. IPFML documentation master file, created by
+   sphinx-quickstart on Fri Jan  4 12:10:59 2019.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+IPFML's documentation!
+=================================
+
+.. toctree::
+   :maxdepth: 2
+      
+   description
+   ipfml
+   ipfml.filters
+
+What's IPFML ?
+=================
+
+Image Processing For Machine Learning python package.
+
+This is a package developed during a thesis project.
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`

+ 10 - 0
docs/source/ipfml.filters.rst

@@ -0,0 +1,10 @@
+ipfml.filters
+=====================
+
+ipfml.filters.noise
+--------------------------
+
+.. automodule:: ipfml.filters.noise
+    :members:
+    :show-inheritance:
+    

+ 16 - 0
docs/source/ipfml.rst

@@ -0,0 +1,16 @@
+ipfml
+=============
+
+ipfml.metrics
+--------------------
+
+.. automodule:: ipfml.metrics
+    :members:
+    :show-inheritance:
+
+ipfml.processing
+-----------------------
+
+.. automodule:: ipfml.processing
+    :members:
+    :show-inheritance:

+ 0 - 0
ipfml/filters/__init__.py


+ 308 - 0
ipfml/filters/noise.py

@@ -0,0 +1,308 @@
+import numpy as np
+from ipfml import processing
+
+
+def _global_noise_filter(image,
+                         n,
+                         generator,
+                         updator,
+                         identical=False,
+                         distribution_interval=(-0.5, 0.5),
+                         k=0.2):
+    """White noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        generator: lambda function used to generate random numpy array with specific distribution
+        updator: lambda function used to update pixel value
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: tuple which set the distribution interval of uniform distribution (default (-0.5, 0.5))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.2)
+
+    Returns:
+        2D Numpy array with specified noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import _global_noise_filter as gf
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> generator = lambda x: np.random.uniform(-0.5, 0.5, x)
+    >>> updator = lambda x, n, k, noise: x + n * k * noise
+    >>> noisy_image = gf(image, 10, generator, updator)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    image_array = np.asarray(image)
+    nb_chanel = 1
+
+    if image_array.ndim != 3:
+        width, height = image_array.shape
+    else:
+        width, height, nb_chanel = image_array.shape
+
+    a, b = distribution_interval
+    nb_pixels = width * height
+
+    if identical:
+        noise_filter = generator(nb_pixels)
+
+    # final output numpy array
+    output_array = []
+
+    for chanel in range(0, nb_chanel):
+
+        # getting flatten information from image and noise
+        if nb_chanel == 3:
+            image_array_flatten = image_array[:, :, chanel].reshape(nb_pixels)
+        else:
+            image_array_flatten = image_array.reshape(nb_pixels)
+
+        # redefine noise if necessary
+        if not identical:
+            noise_filter = generator(nb_pixels)
+
+        # compute new pixel value
+        # n * k * white_noise_filter[i]
+        noisy_image = np.asarray([
+            updator(image_array_flatten[i], n, k, noise_filter[i])
+            for i in range(0, nb_pixels)
+        ])
+
+        # reshape and normalize new value
+        noisy_image = noisy_image.reshape((width, height))
+
+        noisy_image = np.asarray(noisy_image, 'uint8')
+
+        # in order to concatenae output array
+        if nb_chanel == 3:
+            noisy_image = noisy_image[:, :, np.newaxis]
+
+        # append new chanel
+        output_array.append(noisy_image)
+
+    # concatenate RGB image
+    if nb_chanel == 3:
+        output_array = np.concatenate(output_array, axis=2)
+    else:
+        output_array = np.asarray(output_array).reshape(width, height)
+
+    return output_array
+
+
+def white_noise(image,
+                n,
+                identical=False,
+                distribution_interval=(-0.5, 0.5),
+                k=0.2):
+    """White noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: set the distribution interval of normal law distribution (default (-0.5, 0.5))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.2)
+
+    Returns:
+        2D Numpy array with white noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import white_noise
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> noisy_image = white_noise(image, 10)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    a, b = distribution_interval
+    generator = lambda x: np.random.uniform(a, b, x)
+
+    updator = lambda x, n, k, noise: x + n * k * noise
+
+    return _global_noise_filter(image, n, generator, updator, identical,
+                                distribution_interval, k)
+
+
+def gaussian_noise(image,
+                   n,
+                   identical=False,
+                   distribution_interval=(0, 1),
+                   k=0.1):
+    """Gaussian noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: set the distribution interval of normal law distribution (default (0, 1))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.1)
+
+    Returns:
+        2D Numpy array with gaussian noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import gaussian_noise
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> noisy_image = gaussian_noise(image, 10)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    a, b = distribution_interval
+    generator = lambda x: np.random.normal(a, b, x)
+
+    updator = lambda x, n, k, noise: x + n * k * noise
+
+    return _global_noise_filter(image, n, generator, updator, identical,
+                                distribution_interval, k)
+
+
+def laplace_noise(image,
+                  n,
+                  identical=False,
+                  distribution_interval=(0, 1),
+                  k=0.1):
+    """Laplace noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: set the distribution interval of normal law distribution (default (0, 1))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.1)
+
+    Returns:
+        2D Numpay array with Laplace noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import laplace_noise
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> noisy_image = laplace_noise(image, 10)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    a, b = distribution_interval
+    generator = lambda x: np.random.laplace(a, b, x)
+
+    updator = lambda x, n, k, noise: x + n * k * noise
+
+    return _global_noise_filter(image, n, generator, updator, identical,
+                                distribution_interval, k)
+
+
+def cauchy_noise(image,
+                 n,
+                 identical=False,
+                 distribution_interval=(0, 1),
+                 k=0.0002):
+    """Cauchy noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: set the distribution interval of normal law distribution (default (0, 1))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.0002)
+
+    Returns:
+        2D Numpy array with Cauchy noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import cauchy_noise
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> noisy_image = cauchy_noise(image, 10)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    a, b = distribution_interval
+    generator = lambda x: np.random.standard_cauchy(x)
+
+    updator = lambda x, n, k, noise: x + n * k * noise
+
+    return _global_noise_filter(image, n, generator, updator, identical,
+                                distribution_interval, k)
+
+
+def log_normal_noise(image,
+                     n,
+                     identical=False,
+                     distribution_interval=(0, 1),
+                     k=0.05):
+    """Log-normal noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: set the distribution interval of normal law distribution (default (0, 1))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.05)
+
+    Returns:
+        2D Numpy array with Log-normal noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import log_normal_noise
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> noisy_image = log_normal_noise(image, 10)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    a, b = distribution_interval
+    generator = lambda x: np.random.lognormal(a, b, x)
+
+    updator = lambda x, n, k, noise: x + n * k * noise
+
+    return _global_noise_filter(image, n, generator, updator, identical,
+                                distribution_interval, k)
+
+
+def mut_white_noise(image,
+                    n,
+                    identical=False,
+                    distribution_interval=(-0.5, 0.5),
+                    k=0.2):
+    """Multiplied White noise filter to apply on image
+
+    Args:
+        image: image used as input (2D or 3D image representation)
+        n: used to set importance of noise [1, 999]
+        identical: keep or not identical noise distribution for each canal if RGB Image (default False)
+        distribution_interval: set the distribution interval of normal law distribution (default (-0.5, 0.5))
+        k: variable that specifies the amount of noise to be taken into account in the output image (default 0.2)
+
+    Returns:
+        2D Numpy array with multiplied white noise applied
+
+    Usage:
+
+    >>> from ipfml.filters.noise import mut_white_noise
+    >>> import numpy as np
+    >>> image = np.random.uniform(0, 255, 10000).reshape((100, 100))
+    >>> noisy_image = mut_white_noise(image, 10)
+    >>> noisy_image.shape
+    (100, 100)
+    """
+
+    a, b = distribution_interval
+    generator = lambda x: np.random.uniform(a, b, x)
+
+    updator = lambda x, n, k, noise: x * n * k * noise
+
+    return _global_noise_filter(image, n, generator, updator, identical,
+                                distribution_interval, k)

+ 0 - 410
ipfml/image_processing.py

@@ -1,410 +0,0 @@
-from PIL import Image
-from matplotlib import cm
-import random
-
-from skimage import color
-import numpy as np
-import ipfml.metrics as metrics
-import cv2
-
-from scipy import signal
-
-def get_LAB_L_SVD(image):
-    """
-    @brief Returns Singular values from LAB L Image information
-    @param fig a matplotlib figure
-    @return a Python Imaging Library (PIL) image : default size (480,640,3)
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> U, s, V = image_processing.get_LAB_L_SVD(img)
-    >>> U.shape
-    (200, 200)
-    >>> len(s)
-    200
-    >>> V.shape
-    (200, 200)
-    """
-    L = metrics.get_LAB_L(image)
-    return metrics.get_SVD(L)
-
-def get_LAB_L_SVD_s(image):
-    """
-    @brief Returns s (Singular values) SVD from L of LAB Image information
-    @param PIL Image
-    @return vector of singular values
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> s = image_processing.get_LAB_L_SVD_s(img)
-    >>> len(s)
-    200
-    """
-    L = metrics.get_LAB_L(image)
-    return metrics.get_SVD_s(L)
-
-def get_LAB_L_SVD_U(image):
-    """
-    @brief Returns U SVD from L of LAB Image information
-    @param PIL Image
-    @return vector of singular values
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> U = image_processing.get_LAB_L_SVD_U(img)
-    >>> U.shape
-    (200, 200)
-    """
-    L = metrics.get_LAB_L(image)
-    return metrics.get_SVD_U(L)
-
-def get_LAB_L_SVD_V(image):
-    """
-    @brief Returns V SVD from L of LAB Image information
-    @param PIL Image
-    @return vector of singular values
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> V = image_processing.get_LAB_L_SVD_V(img)
-    >>> V.shape
-    (200, 200)
-    """
-
-    L = metrics.get_LAB_L(image)
-    return metrics.get_SVD_V(L)
-
-def divide_in_blocks(image, block_size, pil=True):
-    '''
-    @brief Divide image into equal size blocks
-    @param img - PIL Image or numpy array
-    @param block - tuple (width, height) representing the size of each dimension of the block
-    @param pil - kind block type (PIL by default or Numpy array)
-    @return list containing all 2D numpy blocks (in RGB or not)
-
-    Usage :
-
-    >>> import numpy as np
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> from ipfml import metrics
-    >>> image_values = np.random.randint(255, size=(800, 800, 3))
-    >>> blocks = divide_in_blocks(image_values, (20, 20))
-    >>> len(blocks)
-    1600
-    >>> blocks[0].width
-    20
-    >>> blocks[0].height
-    20
-    >>> img_l = Image.open('./images/test_img.png')
-    >>> L = metrics.get_LAB_L(img_l)
-    >>> blocks_L = divide_in_blocks(L, (100, 100))
-    >>> len(blocks_L)
-    4
-    >>> blocks_L[0].width
-    100
-    '''
-
-    blocks = []
-    mode = 'RGB'
-
-    # convert in numpy array
-    image_array = np.array(image)
-
-    # check dimension of input image
-    if image_array.ndim != 3:
-        mode = 'L'
-        image_width, image_height = image_array.shape
-    else:
-        image_width, image_height, _ = image_array.shape
-
-    # check size compatibility
-    width, height = block_size
-
-    if(image_width % width != 0):
-        raise "Width size issue, block size not compatible"
-
-    if(image_height % height != 0):
-        raise "Height size issue, block size not compatible"
-
-    nb_block_width = image_width / width
-    nb_block_height = image_height / height
-
-    for i in range(int(nb_block_width)):
-
-        begin_x = i * width
-
-        for j in range(int(nb_block_height)):
-
-            begin_y = j * height
-
-            # getting sub block information
-            current_block = image_array[begin_x:(begin_x + width), begin_y:(begin_y + height)]
-
-            if pil:
-                blocks.append(Image.fromarray(current_block.astype('uint8'), mode))
-            else:
-                blocks.append(current_block)
-
-    return blocks
-
-
-def normalize_arr(arr):
-    '''
-    @brief Normalize data of 1D array shape
-    @param array - array data of 1D shape
-
-    Usage :
-
-    >>> from ipfml import image_processing
-    >>> import numpy as np
-    >>> arr = np.arange(11)
-    >>> arr_normalized = image_processing.normalize_arr(arr)
-    >>> arr_normalized[1]
-    0.1
-    '''
-
-    output_arr = []
-    max_value = max(arr)
-    min_value = min(arr)
-
-    for v in arr:
-         output_arr.append((v - min_value) / (max_value - min_value))
-
-    return output_arr
-
-
-def normalize_arr_with_range(arr, min, max):
-    '''
-    @brief Normalize data of 1D array shape
-    @param array - array data of 1D shape
-
-    Usage :
-
-    >>> from ipfml import image_processing
-    >>> import numpy as np
-    >>> arr = np.arange(11)
-    >>> arr_normalized = image_processing.normalize_arr_with_range(arr, 0, 20)
-    >>> arr_normalized[1]
-    0.05
-    '''
-
-    output_arr = []
-
-    for v in arr:
-        output_arr.append((v - min) / (max - min))
-
-    return output_arr
-
-def normalize_2D_arr(arr):
-    """
-    @brief Return array normalize from its min and max values
-    @param 2D numpy array
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> img_mscn = image_processing.rgb_to_mscn(img)
-    >>> img_normalized = image_processing.normalize_2D_arr(img_mscn)
-    >>> img_normalized.shape
-    (200, 200)
-    """
-
-    # getting min and max value from 2D array
-    max_value = arr.max(axis=1).max()
-    min_value = arr.min(axis=1).min()
-
-    # lambda computation to normalize
-    g = lambda x : (x - min_value) / (max_value - min_value)
-    f = np.vectorize(g)
-
-    return f(arr)
-
-def rgb_to_mscn(image):
-    """
-    @brief Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN)
-    @param 3D RGB image numpy array or PIL RGB image
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> img_mscn = image_processing.rgb_to_mscn(img)
-    >>> img_mscn.shape
-    (200, 200)
-    """
-
-    # check if PIL image or not
-    img_arr = np.array(image)
-
-    # convert rgb image to gray
-    im = np.array(color.rgb2gray(img_arr)*255, 'uint8')
-
-    return metrics.gray_to_mscn(im)
-
-def rgb_to_grey_low_bits(image, bind=15):
-    """
-    @brief Convert RGB Image into grey image using only 4 low bits values
-    @param 3D RGB image numpy array or PIL RGB image
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> low_bits_grey_img = image_processing.rgb_to_grey_low_bits(img)
-    >>> low_bits_grey_img.shape
-    (200, 200)
-    """
-
-    img_arr = np.array(image)
-    grey_block = np.array(color.rgb2gray(img_arr)*255, 'uint8')
-
-    return metrics.get_low_bits_img(grey_block, bind)
-
-def rgb_to_LAB_L_low_bits(image, bind=15):
-    """
-    @brief Convert RGB Image into Lab L channel image using only 4 low bits values
-    @param 3D RGB image numpy array or PIL RGB image
-
-    Usage :
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> low_bits_Lab_l_img = image_processing.rgb_to_LAB_L_low_bits(img)
-    >>> low_bits_Lab_l_img.shape
-    (200, 200)
-    """
-
-    L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
-
-    return metrics.get_low_bits_img(L_block, bind)
-
-def rgb_to_LAB_L_bits(image, interval):
-    """
-    @brief Returns only bits from LAB L canal specified into the interval
-    @param image to convert using this interval of bits value to keep
-    @param interval (begin, end) of bits values
-    @return Numpy array with reduced values
-
-    >>> from PIL import Image
-    >>> from ipfml import image_processing
-    >>> img = Image.open('./images/test_img.png')
-    >>> bits_Lab_l_img = image_processing.rgb_to_LAB_L_bits(img)
-    >>> bits_Lab_l_img.shape
-    (200, 200)
-    """
-
-    L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
-
-    return metrics.get_bits_img(L_block, interval)
-
-# TODO : Check this method too...
-def get_random_active_block(blocks, threshold = 0.1):
-    """
-    @brief Find an active block from blocks and return it (randomly way)
-    @param 2D numpy array
-    @param threshold 0.1 by default
-    """
-
-    active_blocks = []
-
-    for id, block in enumerate(blocks):
-
-        arr = np.asarray(block)
-        variance = np.var(arr.flatten())
-
-        if variance >= threshold:
-            active_blocks.append(id)
-
-    r_id = random.choice(active_blocks)
-
-    return np.asarray(blocks[r_id])
-
-
-# TODO : check this method and check how to use active block
-def segment_relation_in_block(block, active_block):
-    """
-    @brief Return bêta value to quantity relation between central segment and surrouding regions into block
-    @param 2D numpy array
-    """
-
-    if block.ndim != 2:
-        raise "Numpy array dimension is incorrect, expected 2."
-
-
-    # getting middle information of numpy array
-    x, y = block.shape
-
-    if y < 4:
-        raise "Block size too small needed at least (x, 4) shape"
-
-    middle = int(y / 2)
-
-    # get central segments
-    central_segments = block[:, middle-1:middle+1]
-
-    # getting surrouding parts
-    left_part = block[:, 0:middle-1]
-    right_part = block[:, middle+1:]
-    surrounding_parts = np.concatenate([left_part, right_part])
-
-    std_sur = np.std(surrounding_parts.flatten())
-    std_cen = np.std(central_segments.flatten())
-    std_block = np.std(block.flatten())
-
-    std_q = std_cen / std_sur
-
-    # from article, it says that block if affected with noise if (std_block > 2 * beta)
-    beta = abs(std_q - std_block) / max(std_q, std_block)
-
-    return beta
-
-### other way to compute MSCN :
-# TODO : Temp code, check to remove or use it
-
-def normalize_kernel(kernel):
-    return kernel / np.sum(kernel)
-
-def gaussian_kernel2d(n, sigma):
-    Y, X = np.indices((n, n)) - int(n/2)
-    gaussian_kernel = 1 / (2 * np.pi * sigma ** 2) * np.exp(-(X ** 2 + Y ** 2) / (2 * sigma ** 2))
-    return normalize_kernel(gaussian_kernel)
-
-def local_mean(image, kernel):
-    return signal.convolve2d(image, kernel, 'same')
-
-def local_deviation(image, local_mean, kernel):
-    "Vectorized approximation of local deviation"
-    sigma = image ** 2
-    sigma = signal.convolve2d(sigma, kernel, 'same')
-    return np.sqrt(np.abs(local_mean ** 2 - sigma))
-
-def calculate_mscn_coefficients(image, kernel_size=6, sigma=7/6):
-
-    # check if PIL image or not
-    img_arr = np.array(image)
-
-    C = 1/255
-    kernel = gaussian_kernel2d(kernel_size, sigma=sigma)
-    local_mean = signal.convolve2d(img_arr, kernel, 'same')
-    local_var = local_deviation(img_arr, local_mean, kernel)
-
-    return (img_arr - local_mean) / (local_var + C)
-

+ 173 - 108
ipfml/metrics.py

@@ -9,34 +9,17 @@ from skimage import io, color
 
 import cv2
 
-def get_image_path(image):
-    """
-    @brief Returns file path of PIL Image
-    @param PIL Image
-    @return image path
-
-    >>> from PIL import Image
-    >>> from ipfml import metrics
-    >>> img = Image.open('./images/test_img.png')
-    >>> path = metrics.get_image_path(img)
-    >>> 'images/test_img.png' in path
-    True
-    """
 
-    if hasattr(image, 'filename'):
-        file_path = image.filename
-    else:
-        raise Exception("Image provided is not correct, required filename property...")
+def get_SVD(image):
+    """Transforms Image using SVD compression
 
-    return file_path
+    Args:
+        image: image to convert into SVD compression
 
-def get_SVD(image):
-    """
-    @brief Transforms Image into SVD
-    @param image to convert
-    @return U, s, V image decomposition
+    Return:
+        U, s, V obtained from SVD compression
 
-    Usage :
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -51,13 +34,17 @@ def get_SVD(image):
     """
     return svd(image, full_matrices=False)
 
+
 def get_SVD_s(image):
-    """
-    @brief Transforms Image into SVD and returns only 's' part
-    @param image to convert
-    @return s
+    """Transforms Image into SVD and returns only 's' part
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        vector of singular values obtained from SVD compression
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -69,13 +56,17 @@ def get_SVD_s(image):
     U, s, V = svd(image, full_matrices=False)
     return s
 
+
 def get_SVD_U(image):
-    """
-    @brief Transforms Image into SVD and returns only 'U' part
-    @param image to convert
-    @return U
+    """Transforms Image into SVD and returns only 'U' part
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        U matrix from SVD compression
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -88,11 +79,15 @@ def get_SVD_U(image):
     U, s, V = svd(image, full_matrices=False)
     return U
 
+
 def get_SVD_V(image):
-    """
-    @brief Transforms Image into SVD and returns only 'V' part
-    @param image to convert
-    @return V
+    """Transforms Image into SVD and returns only 'V' part
+
+    Args:
+        image: image to convert
+
+    Returns:
+        V matrix obtained from SVD compression
 
     Usage :
 
@@ -107,13 +102,17 @@ def get_SVD_V(image):
     U, s, V = svd(image, full_matrices=False)
     return V
 
+
 def get_LAB(image):
-    """
-    @brief Transforms PIL RGB Image into LAB
-    @param image to convert
-    @return Lab information
+    """Transforms RGB  Image into Lab
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        Lab information
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -125,11 +124,15 @@ def get_LAB(image):
 
     return color.rgb2lab(image)
 
+
 def get_LAB_L(image):
-    """
-    @brief Transforms PIL RGB Image into LAB and returns L
-    @param image to convert
-    @return Lab information
+    """Transforms RGB Image into Lab and returns L
+
+    Args:
+        image: image to convert
+
+    Returns:
+        The L chanel from Lab information
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -142,51 +145,63 @@ def get_LAB_L(image):
     lab = get_LAB(image)
     return lab[:, :, 0]
 
-def get_LAB_A(image):
-    """
-    @brief Transforms PIL RGB Image into LAB and returns A
-    @param image to convert
-    @return Lab information
 
-    Usage :
+def get_LAB_a(image):
+    """Transforms RGB Image into LAB and returns a
+
+    Args:
+        image: image to convert
+
+    Returns:
+        The a chanel from Lab information
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
     >>> img = Image.open('./images/test_img.png')
-    >>> A = metrics.get_LAB_A(img)
-    >>> A.shape
+    >>> a = metrics.get_LAB_a(img)
+    >>> a.shape
     (200, 200)
     """
 
     lab = get_LAB(image)
     return lab[:, :, 1]
 
-def get_LAB_B(image):
-    """
-    @brief Transforms PIL RGB Image into LAB and returns B
-    @param image to convert
-    @return Lab information
+
+def get_LAB_b(image):
+    """Transforms RGB Image into LAB and returns b
+
+    Args:
+        image: image to convert
+
+    Returns:
+        The b chanel from Lab information
 
     Usage :
 
     >>> from PIL import Image
     >>> from ipfml import metrics
     >>> img = Image.open('./images/test_img.png')
-    >>> B = metrics.get_LAB_B(img)
-    >>> B.shape
+    >>> b = metrics.get_LAB_b(img)
+    >>> b.shape
     (200, 200)
     """
 
     lab = get_LAB(image)
     return lab[:, :, 2]
 
+
 def get_XYZ(image):
-    """
-    @brief Transforms PIL RGB Image into XYZ
-    @param image to convert
-    @return Lab information
+    """Transforms RGB Image into XYZ
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        XYZ information obtained from transformation
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -197,13 +212,17 @@ def get_XYZ(image):
 
     return color.rgb2xyz(image)
 
+
 def get_XYZ_X(image):
-    """
-    @brief Transforms PIL RGB Image into XYZ and returns X
-    @param image to convert
-    @return Lab information
+    """Transforms RGB Image into XYZ and returns X
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        The X chanel from XYZ information
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -216,13 +235,17 @@ def get_XYZ_X(image):
     xyz = color.rgb2xyz(image)
     return xyz[:, :, 0]
 
+
 def get_XYZ_Y(image):
-    """
-    @brief Transforms PIL RGB Image into XYZ and returns Y
-    @param image to convert
-    @return Lab information
+    """Transforms RGB Image into XYZ and returns Y
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        The Y chanel from XYZ information
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -235,13 +258,17 @@ def get_XYZ_Y(image):
     xyz = color.rgb2xyz(image)
     return xyz[:, :, 1]
 
+
 def get_XYZ_Z(image):
-    """
-    @brief Transforms PIL RGB Image into XYZ and returns Z
-    @param image to convert
-    @return Lab information
+    """Transforms RGB Image into XYZ and returns Z
 
-    Usage :
+    Args:
+        image: image to convert
+
+    Returns:
+        The Z chanel from XYZ information
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -254,35 +281,55 @@ def get_XYZ_Z(image):
     xyz = color.rgb2xyz(image)
     return xyz[:, :, 2]
 
-def get_low_bits_img(image, bind=15):
-    """
-    @brief Returns Image or Numpy array with data information reduced using only low bits
-    @param image to convert
-    @bind optional : bits to keep using & Bitwise operator
-    @return Numpy array with reduced values
 
-    Usage :
+def get_low_bits_img(image, nb_bits=4):
+    """Returns Image or Numpy array with data information reduced using only low bits
+
+    Args:
+        image: image to convert
+        nb_bits: optional parameter which indicates the number of bits to keep
+
+    Returns:
+        Numpy array with reduced values
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
     >>> img = Image.open('./images/test_img.png')
-    >>> low_bits_img = metrics.get_low_bits_img(img)
+    >>> low_bits_img = metrics.get_low_bits_img(img, 5)
     >>> low_bits_img.shape
     (200, 200, 3)
     """
 
+    if nb_bits <= 0:
+        raise ValueError(
+            "unexpected value of number of bits to keep. @nb_bits needs to be positive and greater than 0."
+        )
+
+    if nb_bits > 8:
+        raise ValueError(
+            "Unexpected value of number of bits to keep. @nb_bits needs to be in interval [1, 8]."
+        )
+
     img_arr = np.array(image)
 
-    return img_arr & bind
+    bits_values = sum([pow(2, i - 1) for i in range(1, nb_bits + 1)])
+
+    return img_arr & bits_values
+
 
 def get_bits_img(image, interval):
-    """
-    @brief Returns only bits specified into the interval
-    @param image to convert using this interval of bits value to keep
-    @param interval (begin, end) of bits values
-    @return Numpy array with reduced values
+    """Returns only bits specified into the interval
 
-    Usage :
+    Args:
+        image: image to convert using this interval of bits value to keep
+        interval: (begin, end) of bits values
+
+    Returns:
+        Numpy array with reduced values
+
+    Usage:
 
     >>> from PIL import Image
     >>> from ipfml import metrics
@@ -295,32 +342,50 @@ def get_bits_img(image, interval):
     img_arr = np.array(image)
     begin, end = interval
 
+    if begin < 1:
+        raise ValueError(
+            "Unexpected value of interval. Interval min value needs to be >= 1."
+        )
+
+    if end > 8:
+        raise ValueError(
+            "Unexpected value of interval. Interval min value needs to be <= 8."
+        )
+
+    if begin >= end:
+        raise ValueError("Unexpected interval values order.")
+
     bits_values = sum([pow(2, i - 1) for i in range(begin, end + 1)])
 
     return img_arr & bits_values
 
 
 def gray_to_mscn(image):
-    """
-    @brief Convert Grayscale Image into Mean Subtracted Contrast Normalized (MSCN)
-    @param grayscale image numpy array or PIL RGB image
+    """Convert Grayscale Image into Mean Subtracted Contrast Normalized (MSCN)
 
-    Usage :
+    Args:
+        image: grayscale image
+
+    Returns:
+        MSCN matrix obtained from transformation
+
+    Usage:
 
     >>> from PIL import Image
-    >>> from ipfml import image_processing
+    >>> from ipfml import processing
     >>> img = Image.open('./images/test_img.png')
-    >>> img_mscn = image_processing.rgb_to_mscn(img)
+    >>> img_mscn = processing.rgb_to_mscn(img)
     >>> img_mscn.shape
     (200, 200)
     """
 
-    s = 7/6
-    blurred = cv2.GaussianBlur(image, (7, 7), s) # apply gaussian blur to the image
+    s = 7 / 6
+    blurred = cv2.GaussianBlur(image, (7, 7),
+                               s)  # apply gaussian blur to the image
     blurred_sq = blurred * blurred
     sigma = cv2.GaussianBlur(image * image, (7, 7), s)
-    sigma = abs(sigma - blurred_sq) ** 0.5
-    sigma = sigma + 1.0/255 # avoid DivideByZero Exception
-    mscn = (image - blurred)/sigma # MSCN(i, j) image
+    sigma = abs(sigma - blurred_sq)**0.5
+    sigma = sigma + 1.0 / 255  # avoid DivideByZero Exception
+    mscn = (image - blurred) / sigma  # MSCN(i, j) image
 
     return mscn

+ 377 - 0
ipfml/processing.py

@@ -0,0 +1,377 @@
+from PIL import Image
+from matplotlib import cm
+import random
+
+from skimage import color
+import numpy as np
+import ipfml.metrics as metrics
+import cv2
+
+from scipy import signal
+
+
+def get_LAB_L_SVD(image):
+    """Returns Singular values from LAB L Image information
+
+    Args:
+        image: PIL Image or Numpy array
+
+    Returns:
+        U, s, V information obtained from SVD compression using Lab
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> U, s, V = processing.get_LAB_L_SVD(img)
+    >>> U.shape
+    (200, 200)
+    >>> len(s)
+    200
+    >>> V.shape
+    (200, 200)
+    """
+    L = metrics.get_LAB_L(image)
+    return metrics.get_SVD(L)
+
+
+def get_LAB_L_SVD_s(image):
+    """Returns s (Singular values) SVD from L of LAB Image information
+
+    Args:
+        image: PIL Image or Numpy array
+
+    Returns:
+        vector of singular values
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> s = processing.get_LAB_L_SVD_s(img)
+    >>> len(s)
+    200
+    """
+    L = metrics.get_LAB_L(image)
+    return metrics.get_SVD_s(L)
+
+
+def get_LAB_L_SVD_U(image):
+    """Returns U SVD from L of LAB Image information
+
+    Args:
+        image: PIL Image or Numpy array
+
+    Returns:
+        U matrix of SVD compression
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> U = processing.get_LAB_L_SVD_U(img)
+    >>> U.shape
+    (200, 200)
+    """
+    L = metrics.get_LAB_L(image)
+    return metrics.get_SVD_U(L)
+
+
+def get_LAB_L_SVD_V(image):
+    """Returns V SVD from L of LAB Image information
+
+    Args:
+        image: PIL Image or Numpy array
+
+    Returns:
+        V matrix of SVD compression
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> V = processing.get_LAB_L_SVD_V(img)
+    >>> V.shape
+    (200, 200)
+    """
+
+    L = metrics.get_LAB_L(image)
+    return metrics.get_SVD_V(L)
+
+
+def divide_in_blocks(image, block_size, pil=True):
+    '''Divide image into equal size blocks
+
+    Args:
+        image: PIL Image or Numpy array
+        block: tuple (width, height) representing the size of each dimension of the block
+        pil: block type returned (default True)
+
+    Returns:
+        list containing all 2D Numpy blocks (in RGB or not)
+
+    Usage:
+
+    >>> import Numpy as np
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> from ipfml import metrics
+    >>> image_values = np.random.randint(255, size=(800, 800, 3))
+    >>> blocks = divide_in_blocks(image_values, (20, 20))
+    >>> len(blocks)
+    1600
+    >>> blocks[0].width
+    20
+    >>> blocks[0].height
+    20
+    >>> img_l = Image.open('./images/test_img.png')
+    >>> L = metrics.get_LAB_L(img_l)
+    >>> blocks_L = divide_in_blocks(L, (100, 100))
+    >>> len(blocks_L)
+    4
+    >>> blocks_L[0].width
+    100
+    '''
+
+    blocks = []
+    mode = 'RGB'
+
+    # convert in Numpy array
+    image_array = np.array(image)
+
+    # check dimension of input image
+    if image_array.ndim != 3:
+        mode = 'L'
+        image_width, image_height = image_array.shape
+    else:
+        image_width, image_height, _ = image_array.shape
+
+    # check size compatibility
+    width, height = block_size
+
+    if (image_width % width != 0):
+        raise "Width size issue, block size not compatible"
+
+    if (image_height % height != 0):
+        raise "Height size issue, block size not compatible"
+
+    nb_block_width = image_width / width
+    nb_block_height = image_height / height
+
+    for i in range(int(nb_block_width)):
+
+        begin_x = i * width
+
+        for j in range(int(nb_block_height)):
+
+            begin_y = j * height
+
+            # getting sub block information
+            current_block = image_array[begin_x:(begin_x + width), begin_y:(
+                begin_y + height)]
+
+            if pil:
+                blocks.append(
+                    Image.fromarray(current_block.astype('uint8'), mode))
+            else:
+                blocks.append(current_block)
+
+    return blocks
+
+
+def normalize_arr(arr):
+    '''Normalize data of 1D array shape
+
+    Args:
+        arr: array data of 1D shape
+
+    Returns:
+        Normalized 1D array
+
+    Usage:
+
+    >>> from ipfml import processing
+    >>> import Numpy as np
+    >>> arr = np.arange(11)
+    >>> arr_normalized = processing.normalize_arr(arr)
+    >>> arr_normalized[1]
+    0.1
+    '''
+
+    output_arr = []
+    max_value = max(arr)
+    min_value = min(arr)
+
+    for v in arr:
+        output_arr.append((v - min_value) / (max_value - min_value))
+
+    return output_arr
+
+
+def normalize_arr_with_range(arr, min, max):
+    '''Normalize data of 1D array shape
+
+    Args:
+        arr: array data of 1D shape
+
+    Returns:
+        Normalized 1D Numpy array
+
+    Usage:
+
+    >>> from ipfml import processing
+    >>> import Numpy as np
+    >>> arr = np.arange(11)
+    >>> arr_normalized = processing.normalize_arr_with_range(arr, 0, 20)
+    >>> arr_normalized[1]
+    0.05
+    '''
+
+    output_arr = []
+
+    for v in arr:
+        output_arr.append((v - min) / (max - min))
+
+    return output_arr
+
+
+def normalize_2D_arr(arr):
+    """Return array normalize from its min and max values
+
+    Args:
+        arr: 2D Numpy array
+
+    Returns:
+        Normalized 2D Numpy array
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> img_mscn = processing.rgb_to_mscn(img)
+    >>> img_normalized = processing.normalize_2D_arr(img_mscn)
+    >>> img_normalized.shape
+    (200, 200)
+    """
+
+    # getting min and max value from 2D array
+    max_value = arr.max(axis=1).max()
+    min_value = arr.min(axis=1).min()
+
+    # normalize each row
+    output_array = []
+    width, height = arr.shape
+
+    for row_index in range(0, height):
+        values = arr[row_index, :]
+        output_array.append(
+            normalize_arr_with_range(values, min_value, max_value))
+
+    return np.asarray(output_array)
+
+
+def rgb_to_mscn(image):
+    """Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN)
+
+    Args:
+        image: 3D RGB image Numpy array or PIL RGB image
+
+    Returns:
+        2D Numpy array with MSCN information
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> img_mscn = processing.rgb_to_mscn(img)
+    >>> img_mscn.shape
+    (200, 200)
+    """
+
+    # check if PIL image or not
+    img_arr = np.array(image)
+
+    # convert rgb image to gray
+    im = np.array(color.rgb2gray(img_arr) * 255, 'uint8')
+
+    return metrics.gray_to_mscn(im)
+
+
+def rgb_to_grey_low_bits(image, nb_bits=4):
+    """Convert RGB Image into grey image using only 4 low bits values
+
+    Args:
+        image: 3D RGB image Numpy array or PIL RGB image
+        nb_bits: optional parameter which indicates the number of bits to keep (default 4)
+
+    Returns:
+        2D Numpy array with low bits information kept
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> low_bits_grey_img = processing.rgb_to_grey_low_bits(img, 5)
+    >>> low_bits_grey_img.shape
+    (200, 200)
+    """
+
+    img_arr = np.array(image)
+    grey_block = np.array(color.rgb2gray(img_arr) * 255, 'uint8')
+
+    return metrics.get_low_bits_img(grey_block, nb_bits)
+
+
+def rgb_to_LAB_L_low_bits(image, nb_bits=4):
+    """Convert RGB Image into Lab L channel image using only 4 low bits values
+
+    Args:
+        image: 3D RGB image Numpy array or PIL RGB image
+        nb_bits: optional parameter which indicates the number of bits to keep (default 4)
+
+    Returns:
+        2D Numpy array with low bits information kept
+
+    Usage:
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> low_bits_Lab_l_img = processing.rgb_to_LAB_L_low_bits(img, 5)
+    >>> low_bits_Lab_l_img.shape
+    (200, 200)
+    """
+
+    L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
+
+    return metrics.get_low_bits_img(L_block, nb_bits)
+
+
+def rgb_to_LAB_L_bits(image, interval):
+    """Returns only bits from LAB L canal specified into the interval
+
+    Args:
+        image: image to convert using this interval of bits value to keep
+        interval: (begin, end) of bits values
+
+    Returns:
+        2D Numpy array with reduced values
+
+    >>> from PIL import Image
+    >>> from ipfml import processing
+    >>> img = Image.open('./images/test_img.png')
+    >>> bits_Lab_l_img = processing.rgb_to_LAB_L_bits(img, (2, 6))
+    >>> bits_Lab_l_img.shape
+    (200, 200)
+    """
+
+    L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
+
+    return metrics.get_bits_img(L_block, interval)

+ 46 - 33
setup.py

@@ -6,46 +6,59 @@ def readme():
         return f.read()
 
 class BuildTestCommand(setuptools.command.build_py.build_py):
-  """Custom build command."""
+    """Custom build command."""
 
-  def run(self):
+    def run(self):
 
-    # run tests using doctest
-    import doctest
-    from ipfml import image_processing
-    from ipfml import metrics
+        # run tests using doctest
+        import doctest
+        from ipfml import processing
+        from ipfml import metrics
+        from ipfml.filters import noise as noise_filters
 
-    doctest.testmod(image_processing)
-    doctest.testmod(metrics)
+        print("Run test command...")
+        doctest.testmod(processing)
+        doctest.testmod(metrics)
+        doctest.testmod(noise_filters)
 
-    setuptools.command.build_py.build_py.run(self)
+        # Run format code using ypaf
+        try:
+            print("Run format code command...")
+            self.spawn(['yapf', '-ir', '-vv', 'ipfml'])
+        except RuntimeError:
+            self.warn('format pakcage code failed')
 
-setup(name='IPFML',
-      version='0.1.5',
-      description='Image Processing For Machine Learning',
-      long_description=readme(),
-      classifiers=[
+        setuptools.command.build_py.build_py.run(self)
+
+
+setup(
+    name='ipfml',
+    version='0.1.6',
+    description='Image Processing For Machine Learning',
+    long_description=readme(),
+    classifiers=[
         'Development Status :: 3 - Alpha',
         'License :: OSI Approved :: MIT License',
         'Programming Language :: Python :: 3.6',
         'Topic :: Scientific/Engineering :: Artificial Intelligence'
-      ],
-      url='https://gogs.univ-littoral.fr/jerome.buisine/IPFML',
-      author='Jérôme BUISINE',
-      author_email='jerome.buisine@univ-littoral.fr',
-      license='MIT',
-      packages=['ipfml'],
-      install_requires=[
-          'matplotlib',
-          'numpy',
-          'Pillow',
-          'sklearn',
-          'scikit-image',
-          'scipy',
-          'opencv-python',
-          'scipy'
-      ],
-      cmdclass={
+    ],
+    url='https://gogs.univ-littoral.fr/jerome.buisine/IPFML',
+    author='Jérôme BUISINE',
+    author_email='jerome.buisine@univ-littoral.fr',
+    license='MIT',
+    packages=['ipfml'],
+    install_requires=[
+        'matplotlib',
+        'numpy',
+        'Pillow',
+        'sklearn',
+        'scikit-image',
+        'scipy',
+        'opencv-python',
+        'scipy',
+        'yapf'
+    ],
+    cmdclass={
         'build_py': BuildTestCommand,
-      },
-      zip_safe=False)
+    },
+    zip_safe=False)