Example #1
0
        /// <summary>
        /// Begin editing an image.
        /// </summary>
        public static byte[] Begin(Bitmap bitmap, ImageLockMode lockMode, out BitmapData bmpData)
        {
            // Lock full image
            Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

            // Lock the bitmap's bits while we change them.
            bmpData = bitmap.LockBits(rect, lockMode, bitmap.PixelFormat);

            // Create length with number of bytes in image
            byte[] rgbValues = new byte[bmpData.GetByteCount()];

            // Copy the RGB values into the array.
            Marshal.Copy(bmpData.Scan0, rgbValues, 0, rgbValues.Length);

            // Check if monochrome
            if (bitmap.PixelFormat == PixelFormat.Format1bppIndexed)
            {
                // Adjust stride
                bmpData.Stride *= Constants.BitsPerByte;

                // Convert each bit to a separate byte
                return(ImageBytes.BytesToBits(rgbValues));
            }
            else
            {
                return(rgbValues);
            }
        }
Example #2
0
        /// <summary>
        /// Gets (256) array of histogram values for image.
        /// </summary>
        /// <typeparam name="T">Image type to process and return</typeparam>
        /// <param name="image">Image to process</param>
        /// <returns>256 array of histogram values</returns>
        public static int[] GetHistogramValues <T>(T image) where T : Image
        {
            byte[] imageBytes = ImageBytes.FromImage(image, out BitmapData bmpData);

            int pixelDepth = bmpData.GetPixelDepth();

            return(GetHistogramValues(imageBytes, pixelDepth));
        }
Example #3
0
        /// <summary>
        /// Copies pixels from source image to destination.
        /// </summary>
        public static void FromSourceToDestination(Bitmap imageSource, Bitmap imageDestination)
        {
            // Get bytes to copy
            byte[] sourceBytes = ImageBytes.FromImage(imageSource);

            // Lock destination during copy/write
            byte[] destinationBytes = ImageEdit.Begin(imageDestination, out BitmapData bmpDataDest);

            // Copy as many bytes of the image as possible
            int limit = Math.Min(sourceBytes.Length, destinationBytes.Length);

            Array.Copy(sourceBytes, destinationBytes, limit);

            // Release lock on destination
            ImageEdit.End(imageDestination, bmpDataDest, destinationBytes);
        }
Example #4
0
        /// <summary>
        /// Finished editing an image.
        /// (Copies new byte values to image and unlocks bitmap)
        /// </summary>
        public static void End(Bitmap bitmap, BitmapData bmpData, byte[] rgbValues)
        {
            // Convert monochrome back to byte array
            if (bitmap.PixelFormat == PixelFormat.Format1bppIndexed)
            {
                // Restore stride
                bmpData.Stride /= Constants.BitsPerByte;

                // Consolidate to bytes
                rgbValues = ImageBytes.BitsToBytes(rgbValues);
            }

            // Copy the RGB values back to the bitmap
            Marshal.Copy(rgbValues, 0, bmpData.Scan0, rgbValues.Length);

            // Unlock the bits.
            bitmap.UnlockBits(bmpData);
        }
        /// <summary>
        /// Improves contrast by stretching or equalization (decision based on current image values).
        /// </summary>
        /// <typeparam name="T">Image type to process and return</typeparam>
        /// <param name="image">Image to enhance</param>
        /// <returns>Image with enhanced contrast</returns>
        public static T Enhance <T>(T image) where T : Image
        {
            Tuple <byte, byte> minMax = ImageBytes.GetMinMaxValue(image);

            double percentRoomToStretch = (double)(minMax.Item1 + (byte.MaxValue - minMax.Item2)) / byte.MaxValue;

            // If space in light/dark areas, then stretch to fill
            // (Retains contrast levels)
            if (percentRoomToStretch > 0.2F)
            {
                return(Stretch(image));
            }
            else
            {
                // Otherwise equalize whole image
                return(HistogramEqualization(image));
            }
        }
Example #6
0
        /// <summary>
        /// Converts image to byte array of 0's and 1's.
        /// </summary>
        /// <param name="image">Image to convert</param>
        /// <param name="threshold">Binary threshold</param>
        /// <returns>byte array of 0's and 1's</returns>
        public static byte[] AsBytes(Image image, byte threshold)
        {
            byte[] imageBytes = ImageBytes.FromImage(image, out BitmapData bmpData);

            // If original image color, now only require 1 byte per image
            byte[] binaryBytes;

            if (bmpData.IsColor())
            {
                // If original image color, now only require 1 byte per image
                binaryBytes = new byte[bmpData.Width * bmpData.Height];

                int pixelDepth = bmpData.GetPixelDepth();

                int stride = bmpData.Stride;
                int width  = bmpData.GetStrideWithoutPadding();
                int height = bmpData.Height;
                int limit  = bmpData.GetSafeArrayLimitForImage(imageBytes);
                int index  = 0;

                // Get 0 or 1 for each pixel
                for (int y = 0; y < height; y++)
                {
                    // Images may have extra bytes per row to pad for CPU addressing.
                    // so need to ensure we traverse to the correct byte when moving between rows.
                    // I.e. row width not divisible by 3
                    int offset = y * stride;

                    for (int x = 0; x < width; x += pixelDepth)
                    {
                        int i = offset + x;

                        if (i < limit && index < binaryBytes.Length)
                        {
                            int sum = 0;

                            // Check if any component has value
                            for (int j = 0; j < Constants.PixelDepthRGB && i + j < imageBytes.Length; j++)
                            {
                                sum += imageBytes[i + j];
                            }

                            // Set binary value
                            binaryBytes[index++] = (sum / Constants.PixelDepthRGB) < threshold ? Constants.Zero : Constants.One;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            else
            {
                // Grayscale
                binaryBytes = new byte[imageBytes.Length];

                // Get 0 or 1 for each pixel
                for (int i = 0; i < binaryBytes.Length; i++)
                {
                    // Set binary value
                    binaryBytes[i] = imageBytes[i] < threshold ? Constants.Zero : Constants.One;
                }
            }

            return(binaryBytes);
        }
        /// <summary>
        /// Combines multiple images together.
        /// (Pixel values combined via bitwise or, not averaged)
        /// </summary>
        /// <param name="images">Images to combine</param>
        /// <returns>New combined image as bitmap</returns>
        public static Bitmap All<T>(IEnumerable<T> images) where T : Image
        {
            // Check have at least 1 image
            if (!images.Any())
            {
                return null;
            }

            // Use first image as starting point
            Bitmap combinedImage = ImageFormatting.ToBitmap(images.ElementAt(0));

            // Get bytes for image
            byte[] rgbValues1 = ImageEdit.Begin(combinedImage, out BitmapData bmpData1);

            try
            {
                // Get image 1 data
                int pixelDepth1 = bmpData1.GetPixelDepth();
                int pixelDepthWithoutAlpha = Math.Min(pixelDepth1, Constants.PixelDepthRGB);
                int stride1 = bmpData1.Stride;
                int width1 = bmpData1.GetStrideWithoutPadding();
                int height1 = bmpData1.Height;

                // Add additional images to first
                foreach (Image image in images.Skip(1))
                {
                    // Only reading this image
                    byte[] rgbValues2 = ImageBytes.FromImage(image, out BitmapData bmpData2);

                    // Check both images are color or B&W
                    if (pixelDepth1 == bmpData2.GetPixelDepth())
                    {
                        // Protect against different sized images
                        int limit = Math.Min(bmpData1.GetSafeArrayLimitForImage(rgbValues1), bmpData2.GetSafeArrayLimitForImage(rgbValues2));
                        int minHeight = Math.Min(height1, bmpData2.Height);
                        int minStride = Math.Min(stride1, bmpData2.Stride);
                        int minWidth = Math.Min(width1, bmpData2.GetStrideWithoutPadding());

                        for (int y = 0; y < minHeight; y++)
                        {
                            // Images may have extra bytes per row to pad for CPU addressing.
                            // so need to ensure we traverse to the correct byte when moving between rows.
                            int offset = y * minStride;

                            for (int x = 0; x < minWidth; x += pixelDepth1)
                            {
                                int i = offset + x;

                                if (i < limit)
                                {
                                    for (int j = 0; j < pixelDepthWithoutAlpha; j++)
                                    {
                                        // Combine images
                                        rgbValues1[i + j] |= rgbValues2[i + j];
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new ArgumentException("Not all images have the same color depth");
                    }    
                }
            }
            finally
            {
                // Release combined image
                ImageEdit.End(combinedImage, bmpData1, rgbValues1);
            }

            return combinedImage;
        }