context: /*
   * Vision.NET 2.1 Computer Vision Library
   * Copyright (C) 2009 Matthew Johnson
   * 
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   * 
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   * 
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  using System;
  using System.Windows.Media;
  using System.Windows.Media.Imaging;
  
  namespace VisionNET
  {
      /// <summary>
      /// A grayscale image, with real-valued pixels.
      /// </summary>
      [Serializable]
      public sealed class GrayscaleImage : IMultichannelImage<float>
      {
          private FloatArrayHandler _handler = new FloatArrayHandler();
          private string _label;
  
          /// <summary>
          /// Label for the image.
          /// </summary>
          public string ID
          {
              get
              {
                  return _label;
              }
              set
              {
                  _label = value;
              }
          }
  
          /// <summary>
          /// Constructor.  Creates an empty image.
          /// </summary>
          public GrayscaleImage() { }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="values">Values to use for populating this image</param>
          public unsafe GrayscaleImage(float[,] values)
          {
              int rows = values.GetLength(0);
              int columns = values.GetLength(1);
              float[, ,] data = new float[rows, columns, 1];
              fixed (float* src = values, dst = data)
              {
                  int length = rows * columns;
                  float* srcPtr = src;
                  float* dstPtr = dst;
                  while (length-- > 0)
                      *dstPtr++ = *srcPtr++;
              }
              _handler = new FloatArrayHandler(data, false);
          }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="filename">Path to the source image</param>
          public GrayscaleImage(string filename)
              : this(new BitmapImage(new Uri(filename)))
          {
              ID = filename;
          }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="bitmap">Source image</param>
          public GrayscaleImage(BitmapSource bitmap) : this(new RGBImage(bitmap)) { }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="bitmap">Source image</param>
          public GrayscaleImage(System.Drawing.Bitmap bitmap) : this(new RGBImage(bitmap)) { }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="image">Source image</param>
          public unsafe GrayscaleImage(RGBImage image)
          {
              _handler = new FloatArrayHandler(image.Rows, image.Columns, 1);
              fixed (byte* src = image.RawArray)
              {
                  fixed (float* dst = _handler.RawArray)
                  {
                      byte* srcPtr = src;
                      float* dstPtr = dst;
                      int length = Rows * Columns;
                      while (length-- > 0)
                      {
                          int R = *srcPtr++;
                          int G = *srcPtr++;
                          int B = *srcPtr++;
  
                          float min = Math.Min(R, Math.Min(G, B));
                          float max = Math.Max(R, Math.Max(G, B));
                          float val = (min + max) / 2;
                          *dstPtr++ = val / 255;
                      }
                  }
              }
              ID = image.ID;
          }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="image">Source image</param>
          /// <param name="transform">Transform that converts RGB to grayscale</param>
          public unsafe GrayscaleImage(RGBImage image, float[] transform)
          {
              float t0 = transform[0];
              float t1 = transform[1];
              float t2 = transform[2];
              _handler = new FloatArrayHandler(image.Rows, image.Columns, 1);
              fixed (byte* src = image.RawArray)
              {
                  fixed (float* dst = _handler.RawArray)
                  {
                      byte* srcPtr = src;
                      float* dstPtr = dst;
                      int length = Rows * Columns;
                      while (length-- > 0)
                      {
                          int R = *srcPtr++;
                          int G = *srcPtr++;
                          int B = *srcPtr++;
  
                          *dstPtr++ = t0 * R + t1 * G + t2 * B;
                      }
                  }
              }
              ID = image.ID;
          }
  
          /// <summary>
          /// Constructor.
          /// </summary>
          /// <param name="rows">Number of rows</param>
          /// <param name="columns">Number of columns</param>
          public GrayscaleImage(int rows, int columns)
          {
              _handler = new FloatArrayHandler(rows, columns, 1);
          }
  
          /// <summary>
          /// Accesses the value at the specified row and column.
          /// </summary>
          /// <param name="row">The desired row</param>
          /// <param name="column">The desired column</param>
          /// <returns>The value at (row,column)</returns>
          public float this[int row, int column]
          {
              get
              {
                  return _handler[row, column, 0];
              }
              set
              {
                  _handler[row, column, 0] = value;
              }
          }
  
          /// <summary>
          /// Saves this image to the provided filename, detecting the correct file format from the extension.
          /// </summary>
          /// <param name="filename">The path to the destination image</param>
          public void Save(string filename)
          {
              Save(this, filename);
          }
  
          /// <summary>
          /// Saves the image using the provided minimum and maximum values for scaling.
          /// </summary>
          /// <param name="filename">Destination filename</param>
          /// <param name="min">The value to be scaled to 0</param>
          /// <param name="max">The value to be scaled to 255</param>
          public void Save(string filename, float min, float max)
          {
              Save(this, filename, min, max);
          }
  
          /// <summary>
          /// Returns a Bitmap version of this image using the computed minimum and maximum values.
          /// </summary>
          /// <returns>A Bitmap representing this image</returns>
          public unsafe BitmapSource ToBitmap()
          {
              float min, max;
              getMinMax(out min, out max);
              return ToBitmap(min, max);
          }
  
          /// <summary>
          /// Converts this image to an RGB image, scaling the values so that the maximum value corresponds to a brightness of 255 and
          /// the minimum value corresponds to a brightness of 0.
          /// </summary>
          /// <returns>An RGB depiction of this grayscale image</returns>
          public RGBImage ToRGBImage()
          {
              float min, max;
              getMinMax(out min, out max);
              return ToRGBImage(min, max);
          }
  
          /// <summary>
          /// Converts this image to an RGB image, scaling the values so that the maximum value corresponds to a brightness of 255 and
          /// the minimum value corresponds to a brightness of 0.
          /// </summary>
          /// <param name="color">Color to use in tinting the result</param>
          /// <returns>An RGB depiction of this grayscale image</returns>
          public RGBImage ToRGBImage(Color color)
          {
              float min, max;
              getMinMax(out min, out max);
              return ToRGBImage(min, max, color);
          }
  
          /// <summary>
          /// Converts this image to an RGB image, scaling the values so that the maximum value corresponds to a brightness of 255 and
          /// the minimum value corresponds to a brightness of 0.
          /// </summary>
          /// <param name="max">The maximum value to use</param>
          /// <param name="min">The minimum value to use</param>
          /// <returns>An RGB depiction of this grayscale image</returns>
          public RGBImage ToRGBImage(float min, float max)
          {
              return ToRGBImage(min, max, Colors.White);
          }
  
          /// <summary>
          /// Returns a legacy Bitmap version of this image using the computed minimum and maximum values.
          /// </summary>
          /// <returns>A Bitmap representing this image</returns>
          public System.Drawing.Bitmap ToLegacyBitmap()
          {
              float min, max;
              getMinMax(out min, out max);
              return ToRGBImage(min, max).ToLegacyBitmap();
          }
  
          /// <summary>
          /// Converts the image to a legacy bitmap using the provided minimum and maximum values.
          /// </summary>
          /// <param name="min">The value to map to 0</param>
          /// <param name="max">The value to map to 255</param>
          /// <returns>A Bitmap representing this image</returns>
          public System.Drawing.Bitmap ToLegacyBitmap(float min, float max)
          {
              return ToRGBImage(min, max).ToLegacyBitmap();
          }
  
          private unsafe void getMinMax(out float min, out float max)
          {
              min = float.MaxValue;
              max = float.MinValue;
  
gt:             fixed (float* src = _handler.RawArray)