Exemplo n.º 1
0
        // Checks whether the image is completely black
        public static unsafe bool IsBlack(this WriteableBitmap image)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(image, nameof(image));

            // The RGB offsets from the beginning of the pixel (i.e., 0, 1 or 2)
            WriteableBitmapExtensions.GetColorOffsets(image, out int blueOffset, out int greenOffset, out int redOffset);

            // examine only a subset of pixels as otherwise this is an expensive operation
            // check pixels from last to first as most cameras put a non-black status bar or at least non-black text at the bottom of the frame,
            // so reverse order may be a little faster on average in cases of nighttime images with black skies
            // TODO  Calculate pixelStride as a function of image size so future high res images will still be processed quickly.
            byte *currentPixel = (byte *)image.BackBuffer.ToPointer(); // the imageIndex will point to a particular byte in the pixel array
            int   pixelStride  = Constant.ImageValues.DarkPixelSampleStrideDefault;
            int   totalPixels  = image.PixelHeight * image.PixelWidth; // total number of pixels in the image

            for (int pixelIndex = totalPixels - 1; pixelIndex > 0; pixelIndex -= pixelStride)
            {
                // get next pixel of interest
                byte b = *(currentPixel + blueOffset);
                byte g = *(currentPixel + greenOffset);
                byte r = *(currentPixel + redOffset);

                if (r != 0 || b != 0 || g != 0)
                {
                    return(false);
                }
            }
            return(true);
        }
Exemplo n.º 2
0
        // Given three images, return an image that highlights the differences in common betwen the main image and the first image
        // and the main image and a second image.
        public static unsafe WriteableBitmap CombinedDifference(this WriteableBitmap unaltered, WriteableBitmap previous, WriteableBitmap next, byte threshold)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(unaltered, nameof(unaltered));
            ThrowIf.IsNullArgument(previous, nameof(previous));
            ThrowIf.IsNullArgument(next, nameof(next));

            if (WriteableBitmapExtensions.BitmapsMismatched(unaltered, previous) ||
                WriteableBitmapExtensions.BitmapsMismatched(unaltered, next))
            {
                return(null);
            }

            WriteableBitmapExtensions.GetColorOffsets(unaltered, out int blueOffset, out int greenOffset, out int redOffset);

            int   totalPixels      = unaltered.PixelWidth * unaltered.PixelHeight;
            int   pixelSizeInBytes = unaltered.Format.BitsPerPixel / 8;
            byte *unalteredIndex   = (byte *)unaltered.BackBuffer.ToPointer();
            byte *previousIndex    = (byte *)previous.BackBuffer.ToPointer();
            byte *nextIndex        = (byte *)next.BackBuffer.ToPointer();

            byte[] differencePixels = new byte[totalPixels * pixelSizeInBytes];
            int    differenceIndex  = 0;

            for (int pixel = 0; pixel < totalPixels; ++pixel)
            {
                byte b1 = (byte)Math.Abs(*(unalteredIndex + blueOffset) - *(previousIndex + blueOffset));
                byte g1 = (byte)Math.Abs(*(unalteredIndex + greenOffset) - *(previousIndex + greenOffset));
                byte r1 = (byte)Math.Abs(*(unalteredIndex + redOffset) - *(previousIndex + redOffset));

                byte b2 = (byte)Math.Abs(*(unalteredIndex + blueOffset) - *(nextIndex + blueOffset));
                byte g2 = (byte)Math.Abs(*(unalteredIndex + greenOffset) - *(nextIndex + greenOffset));
                byte r2 = (byte)Math.Abs(*(unalteredIndex + redOffset) - *(nextIndex + redOffset));

                byte b = WriteableBitmapExtensions.DifferenceIfAboveThreshold(threshold, b1, b2);
                byte g = WriteableBitmapExtensions.DifferenceIfAboveThreshold(threshold, g1, g2);
                byte r = WriteableBitmapExtensions.DifferenceIfAboveThreshold(threshold, r1, r2);

                byte averageDifference = (byte)((b + g + r) / 3);
                differencePixels[differenceIndex + blueOffset]  = averageDifference;
                differencePixels[differenceIndex + greenOffset] = averageDifference;
                differencePixels[differenceIndex + redOffset]   = averageDifference;

                unalteredIndex  += pixelSizeInBytes;
                previousIndex   += pixelSizeInBytes;
                nextIndex       += pixelSizeInBytes;
                differenceIndex += pixelSizeInBytes;
            }

            WriteableBitmap difference = new WriteableBitmap(BitmapSource.Create(unaltered.PixelWidth, unaltered.PixelHeight, unaltered.DpiX, unaltered.DpiY, unaltered.Format, unaltered.Palette, differencePixels, unaltered.BackBufferStride));

            return(difference);
        }
Exemplo n.º 3
0
        // Given two images, return an image containing the visual difference between them
        public static unsafe WriteableBitmap Subtract(this WriteableBitmap image1, WriteableBitmap image2)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(image1, nameof(image1));
            ThrowIf.IsNullArgument(image2, nameof(image2));

            if (WriteableBitmapExtensions.BitmapsMismatched(image1, image2))
            {
                return(null);
            }
            WriteableBitmapExtensions.GetColorOffsets(image1, out int blueOffset, out int greenOffset, out int redOffset);

            int   totalPixels      = image1.PixelWidth * image1.PixelHeight;
            int   pixelSizeInBytes = image1.Format.BitsPerPixel / 8;
            byte *image1Index      = (byte *)image1.BackBuffer.ToPointer();
            byte *image2Index      = (byte *)image2.BackBuffer.ToPointer();

            byte[] differencePixels = new byte[totalPixels * pixelSizeInBytes];
            int    differenceIndex  = 0;

            for (int pixel = 0; pixel < totalPixels; ++pixel)
            {
                byte b = (byte)Math.Abs(*(image1Index + blueOffset) - *(image2Index + blueOffset));
                byte g = (byte)Math.Abs(*(image1Index + greenOffset) - *(image2Index + greenOffset));
                byte r = (byte)Math.Abs(*(image1Index + redOffset) - *(image2Index + redOffset));

                byte averageDifference = (byte)((b + g + r) / 3);
                differencePixels[differenceIndex + blueOffset]  = averageDifference;
                differencePixels[differenceIndex + greenOffset] = averageDifference;
                differencePixels[differenceIndex + redOffset]   = averageDifference;

                image1Index     += pixelSizeInBytes;
                image2Index     += pixelSizeInBytes;
                differenceIndex += pixelSizeInBytes;
            }

            WriteableBitmap difference = new WriteableBitmap(BitmapSource.Create(image1.PixelWidth, image1.PixelHeight, image1.DpiX, image1.DpiY, image1.Format, image1.Palette, differencePixels, image1.BackBufferStride));

            return(difference);
        }
Exemplo n.º 4
0
        [HandleProcessCorruptedStateExceptions] // Necessary for unsafe code to implement try/catch
        public static unsafe FileSelectionEnum IsDark(this WriteableBitmap image, int darkPixelThreshold, double darkPixelRatio, out double darkPixelFraction, out bool isColor)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(image, nameof(image));

            // The RGB offsets from the beginning of the pixel (i.e., 0, 1 or 2)
            WriteableBitmapExtensions.GetColorOffsets(image, out int blueOffset, out int greenOffset, out int redOffset);

            // various counters that we will use in calculation of image darkness
            int darkPixels      = 0;
            int uncoloredPixels = 0;
            int countedPixels   = 0;

            // There was a nasty bug that occurred when checking for dark pixels on loading, which happened only (I think) on older machines and operating systems.
            // I suspect its due to a threading issue, but it proved impossible to debug as only release (vs. debug) versions would crash
            // The try / catch mitigates this, although there is a chance that the bug may overwrite some vital memory.
            try
            {
                // Examine only a subset of pixels as otherwise this is a very expensive operation
                // TODO: Calculate pixelStride as a function of image resolution so future high res images will still be processed quickly.
                byte *currentPixel     = (byte *)image.BackBuffer.ToPointer(); // the imageIndex will point to a particular byte in the pixel array
                int   pixelSizeInBytes = image.Format.BitsPerPixel / 8;
                int   pixelStride      = Constant.ImageValues.DarkPixelSampleStrideDefault;
                int   totalPixels      = image.PixelHeight * image.PixelWidth; // total number of pixels in the image

                for (int pixelIndex = 0; pixelIndex < totalPixels; pixelIndex += pixelStride)
                {
                    // get next pixel of interest
                    byte b = *(currentPixel + blueOffset);
                    byte g = *(currentPixel + greenOffset);
                    byte r = *(currentPixel + redOffset);

                    // The numbers below convert a particular color to its greyscale equivalent, and then checked against the darkPixelThreshold
                    // Colors are not weighted equally. Since pure green is lighter than pure red and pure blue, it has a higher weight.
                    // Pure blue is the darkest of the three, so it receives the least weight.
                    int humanPercievedLuminosity = (int)Math.Round(0.299 * r + 0.5876 * g + 0.114 * b);
                    if (humanPercievedLuminosity <= darkPixelThreshold)
                    {
                        ++darkPixels;
                    }

                    // Check if the pixel is a grey scale vs. color pixel, using a heuristic.
                    // In precise grey scales, r = g = b. However, we allow a bit of slop as some cameras actually
                    // have a bit of color in their dark shots (don't ask me why, it just happens).
                    // i.e. if the total delta is less than the color slop, then we consider it a grey level.
                    // Given a pixel's rgb values, calculate the delta between all those values. This is used to determine if its a grey scale pixel.
                    // In practice a grey scale pixel's rgb are all equal (i.e., delta = 0) but we need the value as we want to see how 'close' the pixel
                    // actually is to 0, i.e., to allow some slop in determining grey versus color pixels.
                    int rgbDelta = Math.Abs(r - g) + Math.Abs(g - b) + Math.Abs(b - r);
                    if (rgbDelta <= Constant.ImageValues.GreyscalePixelThreshold)
                    {
                        ++uncoloredPixels;
                    }

                    // update other loop variables
                    ++countedPixels;
                    currentPixel += pixelSizeInBytes * pixelStride; // Advance the pointer to the beginning of the next pixel of interest
                }
                // Check if its a grey scale image, i.e., at least 90% of the pixels in this image (given this slop) are grey scale.
                // If not, its a color image so judge it as not dark
                double uncoloredPixelFraction = 1d * uncoloredPixels / countedPixels;
                if (uncoloredPixelFraction < Constant.ImageValues.GreyscaleImageThreshold)
                {
                    darkPixelFraction = 1 - uncoloredPixelFraction;
                    isColor           = true;
                    return(FileSelectionEnum.Ok);
                }

                // It is a grey scale image. If the fraction of dark pixels are higher than the threshold, it is dark.
                darkPixelFraction = 1d * darkPixels / countedPixels;
                isColor           = false;
                if (darkPixelFraction >= darkPixelRatio)
                {
                    return(FileSelectionEnum.Dark);
                }
                return(FileSelectionEnum.Ok);
            }
//#pragma warning disable CA2153 // Do Not Catch Corrupted State Exceptions
            catch
//#pragma warning restore CA2153 // Do Not Catch Corrupted State Exceptions
            {
                // Without this catch, Timelapse will crash on some machines and OS during loads (see notes in method comments)
                // Note that we have to set isColor and darkPixelFraction to some value, although these are nonsensical as
                // the method has not completed.
                // Returning a Corrupted state informs the caller that the check for dark has not completed successfully.
                isColor           = true;
                darkPixelFraction = 0;
                return(FileSelectionEnum.Corrupted);
            }
        }