internal void Calculate <TDepth>(Image <Gray, TDepth>[] channels, bool accumulate, Image <Gray, byte> mask = null)
            where TDepth : struct
        {
            var color = ColorInfo.GetInfo(typeof(Gray), typeof(TDepth));

            CalculateHistFunc calculateHistFunc = null;

            if (calculateHistFuncs.TryGetValue(color.ChannelType, out calculateHistFunc) == false)
            {
                throw new Exception(string.Format("Calculate function does not support an image of type {0}", color.ChannelType));
            }

            if (!accumulate)
            {
                Array.Clear(histogram, 0, this.NumberOfElements);
            }

            if (mask == null)
            {
                mask = new Image <Gray, byte>(channels[0].Width, channels[0].Height);
                mask.SetValue(new Gray(255));
            }

            calculateHistFunc(this, channels, mask);
        }
        /// <summary>
        /// Gets images (channels) for 2D/3D array.
        /// <remarks> Any array that is not 2D/3D and its element is not a primitive type is not supported.  (an exception is thrown)</remarks>
        /// </summary>
        /// <param name="arr">Input array.</param>
        /// <returns>Channels. For 2D array output will consist of an single image.</returns>
        public unsafe static IImage[] GetChannels(this Array arr)
        {
            int nChannels, width, height;

            getArrayDimenions(arr, out nChannels, out width, out height);

            var elemType = arr.GetType().GetElementType();
            var color    = ColorInfo.GetInfo(typeof(Gray), elemType);


            GCHandle arrHandle    = GCHandle.Alloc(arr, GCHandleType.Pinned);
            int      arrDimStride = width * color.Size;

            var imageChannels = new IImage[nChannels];

            for (int i = 0; i < nChannels; i++)
            {
                int    dimOffset = i * arrDimStride * height;
                IntPtr dimPtr    = (IntPtr)((byte *)arrHandle.AddrOfPinnedObject() + dimOffset);

                var channelImg = Image.Create(color, width, height);
                HelperMethods.CopyImage(dimPtr, channelImg.ImageData, arrDimStride, channelImg.Stride, arrDimStride, height);
                imageChannels[i] = channelImg;
            }

            arrHandle.Free();

            return(imageChannels);
        }
Beispiel #3
0
        private static Func <IImage> getGenericImageConstructor(Type objectType, ColorInfo colorInfo)
        {
            var genericClassType = objectType.MakeGenericType(colorInfo.ColorType, colorInfo.ChannelType);
            var ctor             = genericClassType.GetTypeInfo().DeclaredConstructors.Single(c => !c.IsStatic && c.GetParameters().Length == 0);
            var ctorInvoker      = Expression.Lambda <Func <IImage> >(Expression.New(ctor)).Compile();

            return(ctorInvoker);
        }
Beispiel #4
0
        /// <summary>
        /// Creates an generic image from unmanaged data. Data is shared.
        /// </summary>
        /// <param name="colorInfo">Color info.</param>
        /// <param name="imageData">Unmanaged data pointer.</param>
        /// <param name="width">Image width.</param>
        /// <param name="height">Image height.</param>
        /// <param name="stride">Image stride.</param>
        /// <param name="parentReference">To prevent object from deallocating use this parameter.</param>
        /// <param name="parentDestructor">If a parent needs to be destroyed or release use this function. (e.g. unpin object - GCHandle)</param>
        /// <returns>Generic image.</returns>
        public static IImage Create(ColorInfo colorInfo, IntPtr imageData, int width, int height, int stride, object parentReference = null, Action <object> parentDestructor = null)
        {
            var   ctorInvoker = HelperMethods.GetGenericImageConstructor(typeof(Image <,>), colorInfo);
            Image im          = (Image)ctorInvoker();

            Initialize(im, imageData, width, height, stride, parentReference, parentDestructor);
            return(im);
        }
Beispiel #5
0
        /// <summary>
        /// Creates an generic image.
        /// </summary>
        /// <param name="colorInfo">Color info.</param>
        /// <param name="width">Image width.</param>
        /// <param name="height">Image height.</param>
        /// <returns>New generic image (interface).</returns>
        public static IImage Create(ColorInfo colorInfo, int width, int height)
        {
            var   ctorInvoker = HelperMethods.GetGenericImageConstructor(typeof(Image <,>), colorInfo);
            Image im          = (Image)ctorInvoker();

            Initialize(im, width, height);
            return(im);
        }
Beispiel #6
0
        private static Func <IImage> getGenericImageConstructor(Type objectType, ColorInfo colorInfo)
        {
            var genericClassType = objectType.MakeGenericType(colorInfo.ColorType, colorInfo.ChannelType);
            var ctor             = genericClassType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, System.Type.EmptyTypes, null);
            var ctorInvoker      = Expression.Lambda <Func <IImage> >(Expression.New(ctor)).Compile();

            return(ctorInvoker);
        }
        /// <summary>
        /// Converts unmanaged image to generic image. Depending on output format a simple cast may be performed instead of data copy.
        /// <seealso cref="AsImage"/>
        /// </summary>
        /// <param name="unmanagedImage">Unmanaged image.</param>
        /// <param name="copyAlways">Forces data copy even if the cast is enough.</param>
        /// <param name="failIfCannotCast">Fails if an image data must be converted to a format that is supported by UnmanagedImage. Switch <paramref name="copyAlways"/> is omitted.</param>
        /// <returns>Generic image.</returns>
        public static Image <TColor, TDepth> ToImage <TColor, TDepth>(this UnmanagedImage unmanagedImage, bool copyAlways = false, bool failIfCannotCast = false)
            where TColor : IColor
            where TDepth : struct
        {
            IImage im = AsImage(unmanagedImage);

            var convertedImage = ((Image)im).Convert(ColorInfo.GetInfo <TColor, TDepth>(), copyAlways, failIfCannotCast);

            return(convertedImage as Image <TColor, TDepth>);
        }
Beispiel #8
0
 private static void correctValueMapping <TColor>(ref int[] colorArr)
     where TColor : IColor
 {
     if (ColorInfo.GetInfo <TColor, double>().ConversionCodename == "BGR") //TODO (priority: lowest): other way to do that (without harcoding) - converters ?
     {
         var temp = colorArr[0];
         colorArr[0] = colorArr[2];
         colorArr[2] = temp;
     }
 }
Beispiel #9
0
        /// <summary>
        /// Converts a bitmap to an image (copies data).
        /// If an output color is not matched with bitmap pixel format additional conversion may be applied.
        /// </summary>
        /// <param name="bmp">Input bitmap.</param>
        /// <returns>Generic image.</returns>
        public static Image <TColor, TDepth> ToImage <TColor, TDepth>(this Bitmap bmp)
            where TColor : IColor
            where TDepth : struct
        {
            IImage im = ToImage(bmp);

            //convert if necessary
            var convertedImage = ((Image)im).Convert(ColorInfo.GetInfo <TColor, TDepth>(), false);

            return(convertedImage as Image <TColor, TDepth>);
        }
Beispiel #10
0
        /// <summary>
        /// Calculates image stride for the specified parameters.
        /// </summary>
        /// <param name="colorInfo">Color info.</param>
        /// <param name="width">Image width.</param>
        /// <param name="allignment">Data alignment.</param>
        /// <returns>Image stride.</returns>
        protected static int CalculateStride(ColorInfo colorInfo, int width, int allignment = 4)
        {
            int stride = width * colorInfo.Size;

            if (allignment != 0 &&
                stride % allignment != 0)
            {
                stride += (allignment - (stride % allignment));
            }

            return(stride);
        }
Beispiel #11
0
        /// <summary>
        /// Converts color to unmanaged data of type <typeparamref name="TDepth"/>.
        /// </summary>
        /// <typeparam name="TColor">Color type.</typeparam>
        /// <typeparam name="TDepth">Channel type.</typeparam>
        /// <param name="color">Color</param>
        /// <param name="data">Pointer to unmanaged data. Space must be allocated (number of color channels * sizeof(<typeparamref name="TDepth"/>)</param>
        public unsafe static void ColorToPointer <TColor, TDepth>(TColor color, IntPtr data)
            where TColor : IColor
            where TDepth : struct
        {
            var colorInfo = ColorInfo.GetInfo <TColor, TDepth>();

            TDepth[] arr = ColorToArray <TColor, TDepth>(color);

            GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);

            AForge.SystemTools.CopyUnmanagedMemory(data, handle.AddrOfPinnedObject(), colorInfo.Size);
            handle.Free();
        }
Beispiel #12
0
        private static Map <ColorInfo, PixelFormat> initPixelFormatMappings()
        {
            var map = new Map <ColorInfo, PixelFormat>();

            map.Add(ColorInfo.GetInfo <Gray, byte>(), PixelFormat.Format8bppIndexed);
            map.Add(ColorInfo.GetInfo <Gray, short>(), PixelFormat.Format16bppGrayScale);
            map.Add(ColorInfo.GetInfo <Bgr, byte>(), PixelFormat.Format24bppRgb);
            map.Add(ColorInfo.GetInfo <Bgra, byte>(), PixelFormat.Format32bppArgb);
            map.Add(ColorInfo.GetInfo <Bgr, short>(), PixelFormat.Format48bppRgb);
            map.Add(ColorInfo.GetInfo <Bgra, short>(), PixelFormat.Format64bppArgb);

            return(map);
        }
        /// <summary>
        /// Converts array to image (data is copied). Array elements must be primitive types.
        /// </summary>
        /// <param name="arr">Input array</param>
        /// <param name="width">Image width.</param>
        /// <param name="height">Image height.</param>
        /// <returns>Image</returns>
        public unsafe static Image <TColor, byte> ToImage <TColor>(this byte[] arr, int width, int height)
            where TColor : IColor
        {
            var colorSize = ColorInfo.GetInfo <TColor, byte>().Size;
            int srcStride = arr.Length / height;

            var image = new Image <TColor, byte>(width, height);

            fixed(byte *arrPtr = arr)
            {
                HelperMethods.CopyImage((IntPtr)arrPtr, image.ImageData, srcStride, image.Stride, image.Width * colorSize, image.Height);
            }

            return(image);
        }
Beispiel #14
0
        /// <summary>
        /// Converts unmanaged data to color representation.
        /// </summary>
        /// <typeparam name="TColor">Color type.</typeparam>
        /// <typeparam name="TDepth">Channel type.</typeparam>
        /// <param name="data">Pointer to unmanaged data of type: <typeparamref name="TDepth"/>.</param>
        /// <returns>Color</returns>
        public unsafe static TColor PointerToColor <TColor, TDepth>(IntPtr data)
            where TColor : IColor
            where TDepth : struct
        {
            TColor color;

            var colorInfo = ColorInfo.GetInfo <TColor, TDepth>();

            using (PinnedArray <TDepth> arr = new PinnedArray <TDepth>(colorInfo.Size))
            {
                AForge.SystemTools.CopyUnmanagedMemory(arr.Data, data, colorInfo.Size);
                color = ArrayToColor <TColor, TDepth>(arr.Array);
            }

            return(color);
        }
        /// <summary>
        /// Casts iplImage in generic image representation.
        /// </summary>
        /// <param name="iplImage">IplImage structure.</param>
        /// <param name="destructor">Destructor which is called when created generic image is disposed.</param>
        /// <returns>Image.</returns>
        public static unsafe IImage AsImage(this IplImage iplImage, Action <IplImage> destructor = null)
        {
            var depthType = depthAssociations.Reverse[iplImage.ChannelDepth];
            var colorType = colorAssociations.Reverse[iplImage.NumberOfChannels];

            var colorInfo = ColorInfo.GetInfo(colorType, depthType);

            if (destructor != null)
            {
                return(Image.Create(colorInfo, (IntPtr)iplImage.ImageData, iplImage.Width, iplImage.Height, iplImage.WidthStep, (object)iplImage, (x) => destructor((IplImage)x)));
            }
            else
            {
                return(Image.Create(colorInfo, (IntPtr)iplImage.ImageData, iplImage.Width, iplImage.Height, iplImage.WidthStep));
            }
        }
Beispiel #16
0
        /// <summary>
        /// Extracts the specified image channels.
        /// </summary>
        /// <typeparam name="TSrcColor">Source color type.</typeparam>
        /// <typeparam name="TDepth">Channel depth type.</typeparam>
        /// <param name="image">Image.</param>
        /// <param name="area">Working area.</param>
        /// <param name="channelIndices">Channel indicies to extract. If null, all channels are extracted.</param>
        /// <returns>Channel collection.</returns>
        public static unsafe Gray <TDepth>[][,] SplitChannels <TSrcColor, TDepth>(this TSrcColor[,] image, Rectangle area, params int[] channelIndices)
        where TSrcColor : struct, IColor <TDepth>
        where TDepth : struct
        {
            if (channelIndices == null || channelIndices.Length == 0)
            {
                channelIndices = Enumerable.Range(0, ColorInfo.GetInfo <TSrcColor>().ChannelCount).ToArray();
            }

            var channels = new Gray <TDepth> [channelIndices.Length][, ];
            for (int i = 0; i < channelIndices.Length; i++)
            {
                channels[i] = GetChannel <TSrcColor, TDepth>(image, area, channelIndices[i]);
            }

            return(channels);
        }
Beispiel #17
0
        internal static IImage[] SplitChannels(IImage img, params int[] channelIndicies)
        {
            if (channelIndicies == null || channelIndicies.Length == 0)
            {
                channelIndicies = Enumerable.Range(0, img.ColorInfo.NumberOfChannels).ToArray();
            }

            Type      depthType   = img.ColorInfo.ChannelType;
            ColorInfo channelType = ColorInfo.GetInfo(typeof(Gray), depthType);

            SplitImage splitter = null;

            if (splitters.TryGetValue(depthType, out splitter) == false)
            {
                throw new Exception(string.Format("Splitting function can not split image of color depth type {0}", depthType));
            }

            ParallelProcessor <IImage, IImage[]> proc = new ParallelProcessor <IImage, IImage[]>(img.Size,
                                                                                                 () => //called once
            {
                IImage[] channels = new IImage[channelIndicies.Length];
                for (int i = 0; i < channels.Length; i++)
                {
                    channels[i] = Image.Create(channelType, img.Width, img.Height);
                }

                return(channels);
            },

                                                                                                 (IImage src, IImage[] dest, Rectangle area) => //called for every thread
            {
                IImage srcPatch    = src.GetSubRect(area);
                IImage[] destPatch = new IImage[dest.Length];

                for (int i = 0; i < dest.Length; i++)
                {
                    destPatch[i] = dest[i].GetSubRect(area);
                }

                splitter(srcPatch, destPatch, channelIndicies);
            }
                                                                                                 /*,new ParallelOptions { ForceSequential = true}*/);

            return(proc.Process(img));
        }
        internal static Image <Gray, byte> InRange <TColor, TDepth>(this Image <TColor, TDepth> img, TDepth[] minArr, TDepth[] maxArr, byte valueToSet = 255, params int[] channelIndicies)
            where TColor : IColor
            where TDepth : struct
        {
            Type depthType = img.ColorInfo.ChannelType;

            InRangeFunc inRangeFilter = null;

            if (inRangeFilters.TryGetValue(typeof(TDepth), out inRangeFilter) == false)
            {
                throw new Exception(string.Format("InRange function can not process image of color depth type {0}", depthType));
            }

            if (channelIndicies == null || channelIndicies.Length == 0)
            {
                channelIndicies = Enumerable.Range(0, ColorInfo.GetInfo <TColor, TDepth>().NumberOfChannels).ToArray();
            }

            if (channelIndicies.Length > img.ColorInfo.NumberOfChannels)
            {
                throw new Exception("Number of processed channels must not exceed the number of available image channels!");
            }

            ParallelProcessor <IImage, Image <Gray, byte> > proc = new ParallelProcessor <IImage, Image <Gray, byte> >(img.Size,
                                                                                                                       () => //called once
            {
                var destImg = new Image <Gray, byte>(img.Width, img.Height);
                destImg.SetValue(new Gray(255));                                                                              //initialize destination mask.
                return(destImg);
            },

                                                                                                                       (IImage srcImg, Image <Gray, byte> dstImg, Rectangle area) => //called for every thread
            {
                var srcPatch  = srcImg.GetSubRect(area);
                var destPatch = dstImg.GetSubRect(area);
                inRangeFilter(srcPatch, minArr, maxArr, channelIndicies, destPatch, valueToSet);
            }
                                                                                                                       /*,new ParallelOptions { ForceSequential = true}*/);

            var dest = proc.Process(img);

            return(dest as Image <Gray, byte>);
        }
        /// <summary>
        /// Converts array to image (data is shared). Array elements must be primitive types.
        /// </summary>
        /// <param name="arr">Input array</param>
        /// <returns>Image</returns>
        public static Image <Gray, TDepth> AsImage <TDepth>(this TDepth[,] arr)
            where TDepth : struct
        {
            GCHandle arrHandle = GCHandle.Alloc(arr, GCHandleType.Pinned);

            int width  = arr.GetLength(1);
            int height = arr.GetLength(0);
            int stride = ColorInfo.GetInfo <Gray, TDepth>().ChannelSize *width;

            Image <Gray, TDepth> img = new Image <Gray, TDepth>(arrHandle.AddrOfPinnedObject(),
                                                                width, height, stride,
                                                                arr, (_) =>
            {
                if (arrHandle.IsAllocated)
                {
                    arrHandle.Free();
                }
            });

            return(img);
        }
        /// <summary>
        /// Converts the image from source to destination color and depth.
        /// Data may be shared if casting is used. To prevent that set <paramref name="copyAlways"/> to true.
        /// </summary>
        /// <param name="image">Image.</param>
        /// <param name="destColor">Destination color info.</param>
        /// <param name="copyAlways">Forces data copy even if a casting is enough.</param>
        /// <param name="failIfCannotCast">If data copy is needed throws an exception.</param>
        /// <returns>Converted image.</returns>
        public static IImage Convert(this IImage image, ColorInfo destColor, bool copyAlways = false, bool failIfCannotCast = false)
        {
            if (image == null)
            {
                return(null);
            }

            var conversionPath = ColorDepthConverter.GetPath(image.ColorInfo, destColor);

            if (conversionPath == null /*it should never be null*/ || conversionPath.Count == 0)
            {
                throw new Exception(String.Format("Image does not support conversion from {0} to {1}", image.ColorInfo.ColorType, destColor.ColorType));
            }

            if (failIfCannotCast && conversionPath.CopiesData() == true)
            {
                throw new Exception("Fail if cannot cast is set to true: Image data must be copied");
            }

            var convertedIm = ColorDepthConverter.Convert(image, conversionPath.ToArray(), copyAlways);

            return(convertedIm);
        }
Beispiel #21
0
        private Image <Gray, TDepth> BackProject <TDepth>(Image <Gray, TDepth>[] srcs)
            where TDepth : struct
        {
            var destColor = ColorInfo.GetInfo <Gray, TDepth>();

            BackpropagateFunc backpropagateFunc = null;

            if (backpropagateFuncs.TryGetValue(destColor.ChannelType, out backpropagateFunc) == false)
            {
                throw new Exception(string.Format("Back-propagate function does not support an image of type {0}", destColor.ChannelType));
            }

            var imgSize = srcs[0].Size;

            var proc = new ParallelProcessor <IImage[], IImage>(imgSize,
                                                                () => //executed once
            {
                return(Image.Create(destColor, imgSize.Width, imgSize.Height));
            },

                                                                (IImage[] srcImgs, IImage destImg, Rectangle area) => //executed for each thread
            {
                var channelPatches = new IImage[srcImgs.Length];
                for (int i = 0; i < channelPatches.Length; i++)
                {
                    channelPatches[i] = srcImgs[i].GetSubRect(area);
                }

                var projPatch = destImg.GetSubRect(area);

                backpropagateFunc(this, channelPatches, projPatch);
            }
                                                                /*,new ParallelOptions { ForceSequential = true}*/);

            return(proc.Process(srcs) as Image <Gray, TDepth>);
        }
        private unsafe static Array toArray(IImage image)
        {
            Array arr = Array.CreateInstance(image.ColorInfo.ChannelType, image.ColorInfo.NumberOfChannels, image.Height, image.Width);

            var channelColor = ColorInfo.GetInfo(typeof(Gray), image.ColorInfo.ChannelType);

            GCHandle arrHandle    = GCHandle.Alloc(arr, GCHandleType.Pinned);
            int      arrDimStride = image.Width * channelColor.Size;

            IImage[] channels = (image.ColorInfo.NumberOfChannels == 1) ? new IImage[] { image } : ChannelSplitter.SplitChannels(image);

            for (int i = 0; i < channels.Length; i++)
            {
                int    dimOffset = i * arrDimStride * image.Height;
                IntPtr dimPtr    = (IntPtr)((byte *)arrHandle.AddrOfPinnedObject() + dimOffset);

                var channelImg = channels[i];
                HelperMethods.CopyImage(channelImg.ImageData, dimPtr, channelImg.Stride, arrDimStride, arrDimStride, image.Height);
            }

            arrHandle.Free();

            return(arr);
        }
Beispiel #23
0
 /// <summary>
 /// Do not remove! Needed for dynamic image creation via cached expressions.
 /// </summary>
 private Image()
 {
     this.ColorInfo = ColorInfo.GetInfo <TColor, TDepth>(); //an early init is needed during deserialization
 }
        internal static Image <TColor, TDepth> Convolve <TColor, TDepth, TKernel>(this Image <TColor, TDepth> src, Image <Gray, TKernel>[] kernels, ConvolutionBorder options, bool forceSpatialConvolution = false)
            where TColor : IColor
            where TDepth : struct
            where TKernel : struct
        {
            bool useFFT = ShouldUseFFT(kernels) && !forceSpatialConvolution;

            Type[] supportedTypes = null;
            if (useFFT)
            {
                supportedTypes = ParallelFFTConvolution.SupportedTypes;
            }
            else
            {
                supportedTypes = ParallelSpatialConvolution.SupportedTypes;
            }

            supportedTypes = supportedTypes.Where(x => x.Equals(typeof(TKernel))).ToArray();
            if (supportedTypes.Length == 0)
            {
                throw new NotSupportedException(string.Format("Kernel of type {0} is not supported. Used convolution: {1}. Supported types for used convolution: {2}." +
                                                              "Please use different kernel type, or force convolution method.",
                                                              typeof(TKernel).Name,
                                                              (useFFT) ? "FFT" : "Spatial",
                                                              supportedTypes.Select(x => x.Name)));
            }

            /************************************** convert src ********************************/
            var    supportedColors = supportedTypes.Select(x => ColorInfo.GetInfo(typeof(TColor), x)).ToArray();
            var    conversionPath  = ColorConverter.GetPath(src.ColorInfo, supportedColors);
            IImage convertedSrc    = ColorConverter.Convert(src, conversionPath.ToArray(), false);

            if (convertedSrc == null)
            {
                throw new Exception(string.Format("Convolution does not support images of type {0}", src.ColorInfo.ChannelType));
            }
            /************************************** convert src ********************************/

            IImage dest = null;

            if (useFFT)
            {
                dest = ParallelFFTConvolution.Convolve <TColor, TKernel>(convertedSrc as Image <TColor, TKernel>, kernels, options);
            }
            else
            {
                dest = ParallelSpatialConvolution.Convolve(convertedSrc, kernels, options);
            }


            /************************************** convert back ********************************/
            var    backwardConversion = ColorConverter.GetPath(dest.ColorInfo, src.ColorInfo);
            IImage convertedDest      = ColorConverter.Convert(dest, backwardConversion.ToArray(), false);

            if (convertedDest == null)
            {
                throw new Exception(string.Format("Convolution does not support images of type {0}", src.ColorInfo.ChannelType));
            }
            /************************************** convert back ********************************/

            return(convertedDest as Image <TColor, TDepth>);
        }
Beispiel #25
0
        /// <summary>
        /// Creates an unmanaged image by pinning the provided array.
        /// <para>No data is copied.</para>
        /// </summary>
        /// <param name="array">Array to lock.</param>
        /// <returns>Unmanaged image.</returns>
        public static Image <TColor> Lock(TColor[,] array)
        {
            GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
            int      width  = array.GetLength(1);
            int      height = array.GetLength(0);

            var image = new Image <TColor>(handle.AddrOfPinnedObject(), width, height, ColorInfo.GetInfo <TColor>().Size *width,
                                           handle, x => ((GCHandle)x).Free());

            return(image);
        }
Beispiel #26
0
 internal static Func <IImage> GetGenericImageConstructor(Type objectType, ColorInfo colorInfo)
 {
     return(MethodCache.Global.Invoke(getGenericImageConstructor, objectType, colorInfo));
 }
 /// <summary>
 /// Converts the image from source to destination color and depth.
 /// Data may be shared if casting is used. To prevent that set <paramref name="copyAlways"/> to true.
 /// </summary>
 /// <typeparam name="DestColor">Destination color (IColor).</typeparam>
 /// <typeparam name="DestType">Destination type (primitive type).</typeparam>
 /// <param name="image">Image.</param>
 /// <param name="copyAlways">Forces data copy even if a casting is enough.</param>
 /// <param name="failIfCannotCast">If data copy is needed throws an exception.</param>
 /// <returns>Converted image.</returns>
 public static Image <DestColor, DestType> Convert <DestColor, DestType>(this IImage image, bool copyAlways = false, bool failIfCannotCast = false)
     where DestColor : IColor
     where DestType : struct
 {
     return(Convert(image, ColorInfo.GetInfo <DestColor, DestType>(), copyAlways, failIfCannotCast) as Image <DestColor, DestType>);
 }