public void Debug_Sobel() { using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(@"C:\Users\User\Downloads\Bikesgray.jpg")) { actual.Sobel().magnitude.Content.Save("debug.png"); } }
public void FastBitmap_ValidBitmap_ShouldSucceed() { using (SUT.FastBitmap expected = new SUT.FastBitmap(new Bitmap(Image.FromFile(FastBitmap.TestData1_KnownGood)))) { } }
public void RadonTransform_InvalidNumberOfAngles_ThrowsArgument(int numberOfAngles) { using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(FastBitmap.TestData1_KnownGood)) { Assert.Throws <ArgumentException>(() => actual.RadonTransform(numberOfAngles)); } }
public void Max_ValidBitmap_ShouldSucceed() { using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(FastBitmap.TestData4_KnownGood)) { Assert.Equal(146, actual.Max()); } }
public void Blur_ValidParams_ShouldSucceed(double sigma, int weight, string input, string expectedOutput) { using (Bitmap expected = (Bitmap)Image.FromFile(expectedOutput)) using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(input)) { TestUtil.AssertContentsEqual(expected, actual.Blur(sigma, weight).Content); } }
public void ScaleBy_ValidFactor_ShouldSucceed(ushort factor, string input, string expectedOutput) { using (Bitmap expected = (Bitmap)Image.FromFile(expectedOutput)) using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(input)) { TestUtil.AssertContentsEqual(expected, actual.ScaleBy(factor).Content); } }
public void Pow_ValidBitmap_ShouldSucceed() { using (Bitmap expected = new Bitmap(Image.FromFile(TestData1_Pow2_KnownGood))) using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(FastBitmap.TestData1_KnownGood)) { TestUtil.AssertContentsEqual(expected, actual.Pow(2).Content); } }
public void Grayscale_VaryingBitColorDepths_ShouldSucceed(string expectedFile, string actualFile) { using (Bitmap expected = new Bitmap(Image.FromFile(expectedFile))) using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(actualFile)) { TestUtil.AssertContentsEqual(expected, actual.ToGrayscale().Content); } }
public void EdgeDetect_ValidBitmap_ShouldSucceed() { using (SUT.FastBitmap expected = SUT.FastBitmap.FromFile(TestData1_EdgeDetect_KnownGood)) using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(TestData1_KnownGood).EdgeDetect()) { actual.Content.Save("output.png"); TestUtil.AssertContentsEqual(expected.Content, actual.Content); } }
public void Sobel_ValidBitmap_ShouldSucceed() { using (SUT.FastBitmap expected = SUT.FastBitmap.FromFile(FastBitmap.TestData1_Sobel_KnownGood)) using (SUT.FastBitmap actual = SUT.FastBitmap.FromFile(FastBitmap.TestData1_KnownGood).Sobel().magnitude) { actual.Content.Save("sobel.png"); TestUtil.AssertContentsEqual(expected.Content, actual.Content); } }
public void RadonTransform_ValidBitmap_ShouldSucceed() { using (SUT.FastBitmap input = SUT.FastBitmap.FromFile(FastBitmap.TestData1_KnownGood)) { SUT.FastBitmap actual = input.ToGrayscale().Blur(1f, 5); actual = actual.ScaleBy(actual.Max()).Pow(2); actual.Content.Save("testing_before.png"); actual.RadonTransform(actual.Content.Width).Transform.Content.Save("testing_after.png"); } }
/// <summary> /// Returns the largest magnitude in the <see cref="FastBitmap"/>. /// </summary> /// <returns> /// The largest magnitude in the <see cref="FastBitmap"/>. /// </returns> public unsafe ushort Max() { object theLock = new object(); ushort returnValue = 0; using (FastBitmap asGrayscale = this.ToGrayscale()) { int bpp = asGrayscale.bitsPerPixel; FastBitmap.Operation(asGrayscale.Content, (data, scan0) => { Parallel.For(0, data.Height, yPos => { byte localMax = 0; for (int xPos = 0; xPos < data.Width; xPos++) { byte current = *FastBitmap.PixelPointer(scan0, xPos, yPos, data.Stride, bpp); if (current > localMax) { localMax = current; } } lock (theLock) { if (localMax > returnValue) { returnValue = localMax; } } }); }); } return(returnValue); }
/// <summary> /// Performs Sobel edge detection on the <see cref="FastBitmap"/>. /// </summary> /// <param name="computeThetas"> /// True if the thetas should be computed in addition to the resulting <see cref="FastBitmap"/>, and false otherwise. Defaults to false. /// </param> /// <returns> /// A named-field tuple containing the resulting Sobel-filtered <see cref="FastBitmap"/>, and the calculated thetas. If <paramref name="computeThetas"/> was false, then thetas will be null. /// </returns> public unsafe (FastBitmap magnitude, double[, ] thetas) Sobel(bool computeThetas = false) { FastBitmap horizontal = this.KernelOperation(Kernel.HorizontalSobel); FastBitmap vertical = this.KernelOperation(Kernel.VerticalSobel); FastBitmap result = new FastBitmap(horizontal.Content.Width, horizontal.Content.Height); FastBitmap.Operation(result.Content, (resultData, resultScan0) => { FastBitmap.Operation(horizontal.Content, (horizontalData, horizontalScan0) => { FastBitmap.Operation(vertical.Content, (verticalData, verticalScan0) => { for (int yPos = 0; yPos < horizontal.Content.Height; yPos++) { for (int xPos = 0; xPos < horizontal.Content.Width; xPos++) { byte *horizontalR = FastBitmap.PixelPointer( horizontalScan0, xPos, yPos, horizontalData.Stride, horizontal.bitsPerPixel); byte *horizontalG = horizontalR + 1; byte *horizontalB = horizontalR + 2; byte *verticalR = FastBitmap.PixelPointer( verticalScan0, xPos, yPos, verticalData.Stride, vertical.bitsPerPixel); byte *verticalG = verticalR + 1; byte *verticalB = verticalR + 2; byte *resultR = FastBitmap.PixelPointer( resultScan0, xPos, yPos, resultData.Stride, result.bitsPerPixel); byte *resultG = resultR + 1; byte *resultB = resultR + 2; *resultR = (byte)(255 * Math.Sqrt(*horizontalR * *horizontalR + *verticalR * *verticalR) / 360); *resultG = (byte)(255 * Math.Sqrt(*horizontalG * *horizontalG + *verticalG * *verticalG) / 360); *resultB = (byte)(255 * Math.Sqrt(*horizontalB * *horizontalB + *verticalB * *verticalB) / 360); *(resultR + 3) = 255; } } }); }); }, ImageLockMode.WriteOnly); double[,] thetas = null; if (computeThetas) { FastBitmap.Operation(horizontal.Content, (horizontalData, horizontalScan0) => { FastBitmap.Operation(vertical.Content, (verticalData, verticalScan0) => { for (int yPos = 0; yPos < horizontal.Content.Height; yPos++) { for (int xPos = 0; xPos < horizontal.Content.Width; xPos++) { thetas[xPos, yPos] = Math.Atan2( *FastBitmap.PixelPointer( verticalScan0, xPos, yPos, verticalData.Stride, vertical.bitsPerPixel), *FastBitmap.PixelPointer( horizontalScan0, xPos, yPos, horizontalData.Stride, horizontal.bitsPerPixel)); } } }); }); } return(result, thetas); }
/// <summary> /// Performs a Radon transformation on the <see cref="FastBitmap"/>. /// </summary> /// <param name="numberOfAngles"> /// The number of angles to consider. Should be a power of 2. /// </param> /// <returns> /// A <see cref="ProjectionResult"/> representing the produced transform. /// </returns> public unsafe ProjectionResult RadonTransform(int numberOfAngles) { if (numberOfAngles <= 0) { throw new ArgumentException(Resources.InvalidNumberOfAnglesSpecified, nameof(numberOfAngles)); } if ((numberOfAngles & (numberOfAngles - 1)) != 0) { throw new ArgumentException(Resources.NumberOfAnglesShouldBePowerOfTwo, nameof(numberOfAngles)); } FastBitmap buffer = new FastBitmap(new Bitmap(this.Content.Width, this.Content.Height)); using (Graphics graphics = Graphics.FromImage(buffer.Content)) { graphics.FillRectangle(Brushes.Black, 0, 0, buffer.Content.Width, buffer.Content.Height); } int[] pixelsPerLine = new int[numberOfAngles]; int height = buffer.Content.Height; int width = buffer.Content.Width; int diff = Math.Max(height, width); double xCenter = (double)width / 2f; double yCenter = (double)height / 2f; int xOffset = (int)(xCenter + FastBitmap.RoundingFactor(xCenter)); int yOffset = (int)(yCenter + FastBitmap.RoundingFactor(yCenter)); FastBitmap.Operation(this.Content, (data, scan0) => { FastBitmap.Operation(buffer.Content, (subData, subScan0) => { for (int k = 0; k < (numberOfAngles / 4) + 1; k++) { double theta = k * Math.PI / numberOfAngles; double alpha = Math.Tan(theta); for (int x = 0; x < diff; x++) { double y = alpha * (x - xOffset); int yd = (int)(y + FastBitmap.RoundingFactor(y)); if ((yd + yOffset >= 0) && (yd + yOffset < height) && (x < width)) { // Originally: *ptr_radon_map->data(k, x) = img(x, yd + yOffset); *FastBitmap.PixelPointer(subScan0, k, x, subData.Stride, buffer.bitsPerPixel) = *FastBitmap.PixelPointer(scan0, x, yd + yOffset, data.Stride, this.bitsPerPixel); pixelsPerLine[k]++; } if ((yd + xOffset >= 0) && (yd + xOffset < width) && (k != numberOfAngles / 4) && (x < height)) { // Originally: *ptr_radon_map->data(numberOfAngles / 2 - k, x) = img(yd + xOffset, x); *FastBitmap.PixelPointer( subScan0, (numberOfAngles / 2) - k, x, subData.Stride, buffer.bitsPerPixel) = *FastBitmap.PixelPointer(scan0, yd + xOffset, x, data.Stride, this.bitsPerPixel); pixelsPerLine[(numberOfAngles / 2) - k]++; } } } int j = 0; for (int k = 3 * numberOfAngles / 4; k < numberOfAngles; k++) { double theta = k * Math.PI / numberOfAngles; double alpha = Math.Tan(theta); for (int x = 0; x < diff; x++) { double y = alpha * (x - xOffset); int yd = (int)(y + FastBitmap.RoundingFactor(y)); if ((yd + yOffset >= 0) && (yd + yOffset < height) && (x < width)) { // Originally: *ptr_radon_map->data(k, x) = img(x, yd + yOffset); *FastBitmap.PixelPointer(subScan0, k, x, subData.Stride, buffer.bitsPerPixel) = *FastBitmap.PixelPointer(scan0, x, yd + yOffset, data.Stride, this.bitsPerPixel); pixelsPerLine[k]++; } if ((yOffset - yd >= 0) && (yOffset - yd < width) && ((2 * yOffset) - x >= 0) && ((2 * yOffset) - x < height) && (k != (3 * numberOfAngles) / 4)) { // Originally: *ptr_radon_map->data(k - j, x) = img(-yd + yOffset, -(x - yOffset) + yOffset); *FastBitmap.PixelPointer(subScan0, k - j, x, subData.Stride, buffer.bitsPerPixel) = *FastBitmap.PixelPointer( scan0, -yd + yOffset, -(x - yOffset) + yOffset, data.Stride, this.bitsPerPixel); pixelsPerLine[k - j]++; } } j += 2; } }); }); return(new ProjectionResult(buffer, pixelsPerLine)); }
/// <summary> /// Performs a Canny Edge Detect on the <see cref="FastBitmap"/>. /// </summary> /// <returns> /// A <see cref="FastBitmap"/> whose contents represent the edges of the current <see cref="FastBitmap"/>. /// </returns> public unsafe FastBitmap EdgeDetect() { (FastBitmap magnitude, double[,] thetas)edgeDetect = this.Sobel(computeThetas: true); FastBitmap buffer = edgeDetect.magnitude; double[,] angles = edgeDetect.thetas; FastBitmap output = new FastBitmap(new Bitmap(buffer.Content.Width - 2, buffer.Content.Height - 2)); FastBitmap.Operation(output.Content, (outputData, outputScan0) => { FastBitmap.Operation(buffer.Content, (bufferData, bufferScan0) => { int localWidth = output.Content.Width + 1; int localBpp = buffer.bitsPerPixel; for (int yPos = 1; yPos < output.Content.Height + 1; yPos++) { for (int xPos = 1; xPos < localWidth; xPos++) { int prevX = 0; int prevY = 0; int nextX = 0; int nextY = 0; switch (angles[xPos - 1, yPos - 1]) { case 0: prevX = -1; nextX = 1; break; case 1: prevX = -1; nextX = 1; prevY = 1; nextY = -1; break; case 2: prevY = -1; nextY = 1; break; case 3: prevX = -1; nextX = 1; prevY = -1; nextY = 1; break; } byte *outputA = FastBitmap.PixelPointer(outputScan0, xPos - 1, yPos - 1, outputData.Stride, output.bitsPerPixel) + 3; *outputA = 255; byte *currentR = FastBitmap.PixelPointer(bufferScan0, xPos, yPos, bufferData.Stride, localBpp); byte *previousR = FastBitmap.PixelPointer(bufferScan0, xPos + prevX, yPos + prevY, bufferData.Stride, buffer.bitsPerPixel); byte *nextR = FastBitmap.PixelPointer(bufferScan0, xPos + nextX, yPos + nextY, bufferData.Stride, buffer.bitsPerPixel); if (*currentR > *previousR && *currentR > *nextR) { *(outputA - 3) = 255; *(outputA - 2) = 255; *(outputA - 1) = 255; } else { *currentR = 0; *(currentR + 1) = 0; *(currentR + 2) = 0; } } } }); }); return(output); }
public void FromFile_ValidBitmap_ShouldSucceed() { using (SUT.FastBitmap expected = SUT.FastBitmap.FromFile(FastBitmap.TestData1_KnownGood)) { } }