How to count spring coil turns? - c#

In reference to: How to detect and count a spiral's turns
I am not able to get count even in the pixel based calculation also.
If I have attached image how to start with the counting the turns.
I tried the FindContours(); but doesn't quite get the turns segregated which it can't. Also the matchshape() I have the similarity factor but for whole coil.
So I tried as follows for turn count:
public static int GetSpringTurnCount()
{
if (null == m_imageROIed)
return -1;
int imageWidth = m_imageROIed.Width;
int imageHeight = m_imageROIed.Height;
if ((imageWidth <= 0) || (imageHeight <= 0))
return 0;
int turnCount = 0;
Image<Gray, float> imgGrayF = new Image<Gray, float>(imageWidth, imageHeight);
CvInvoke.cvConvert(m_imageROIed, imgGrayF);
imgGrayF = imgGrayF.Laplace(1); // For saving integer overflow.
Image<Gray, byte> imgGray = new Image<Gray, byte>(imageWidth, imageHeight);
Image<Gray, byte> cannyEdges = new Image<Gray, byte>(imageWidth, imageHeight);
CvInvoke.cvConvert(imgGrayF, imgGray);
cannyEdges = imgGray.Copy();
//cannyEdges = cannyEdges.ThresholdBinary(new Gray(1), new Gray(255));// = cannyEdges > 0 ? 1 : 0;
cannyEdges = cannyEdges.Max(0);
cannyEdges /= 255;
Double[] sumRow = new Double[cannyEdges.Cols];
//int sumRowIndex = 0;
int Rows = cannyEdges.Rows;
int Cols = cannyEdges.Cols;
for (int X = 0; X < cannyEdges.Cols; X++)
{
Double sumB = 0;
for (int Y = 0; Y < cannyEdges.Rows; Y ++)
{
//LineSegment2D lines1 = new LineSegment2D(new System.Drawing.Point(X, 0), new System.Drawing.Point(X, Y));
Double pixels = cannyEdges[Y, X].Intensity;
sumB += pixels;
}
sumRow[X] = sumB;
}
Double avg = sumRow.Average();
List<int> turnCountList = new List<int>();
int cnt = 0;
foreach(int i in sumRow)
{
sumRow[cnt] /= avg;
if(sumRow[cnt]>3.0)
turnCountList.Add((int)sumRow[cnt]);
cnt++;
}
turnCount = turnCountList.Count();
cntSmooth = cntSmooth * 0.9f + (turnCount) * 0.1f;
return (int)cntSmooth;
}
I am next trying surf.
==================================================
Edit: Adding samples. If you like it do it.
==================================================
Edit: Tried another algo:
ROI then Rotate ( biggest thin light blue rectangle )
GetMoments() shrink ROI height and position.Y using the moment.
Set the shrinked ROI and ._And() it with a blank image. ( Gray region with green rectangle )
cut the image into half-half.
contour and fit ellipse.
get maximum number of fitted ellipses.
Later will work on better algos and results.

Assuming the bigger cluster of white colour is the spring
--EDIT--
Apply inverse threshold to the picture and fill corners with flood fill algorithm.
Find the rotated bounding box of the biggest white cluster using findContours and minAreaRect
Trace the box longer axis doing the following
for each pixel along the axis trace axis line perpendicular going through current pixel
This line will cross the spring in minimum two points.
Find the point with the bigger distane from the axis
This will create collection on points similar to sine function
Count the peaks or clusters of this collection this will get twice the number of loops.
All this assuming you don't have high noise in the picture.

Related

Excluding small chunks of pixels from Image .Net

I have black image with white lines. Is it possible to exclude chunks of whihte pixels, that are smaller than specific number? For example: change color of chunks of pixels that are made from less than 10 pixels from white to black.
Original Image:
Image on the output(small areas of white pixels are removed):
Right now I work with AForge library for C#, but C++ ways of solving this are also apreciated(Open CV, for example). And hint, on how this functionality might be called are also appreciated.
Without worrying to much about your details, it does seem trivially simple
Use bitmap in 32bits and use LockBits to get scanlines and direct pointer access to the array.
Scan every pixel with 2 for loops
Every time you find one that matches your target color, scan left right and up and down (X) Amount of pixels to determine if it matches your requirements,
If it does, leave the pixel, if not change it.
if you wanted more speed you could chuck this all in a parallel workload, also there is probably more you could do with a mask array to save you researching dead paths (just a thought)
Note, Obviously you can smarten this up a bit
Exmaple
// lock the array for direct access
var bitmapData = bitmap.LockBits(Bounds, ImageLockMode.ReadWrite, Bitmap.PixelFormat);
// get the pointer
var scan0Ptr = (int*)_bitmapData.Scan0;
// get the stride
var stride = _bitmapData.Stride / BytesPerPixel;
// local method
void Workload(Rectangle bounds)
{
// this is if synchronous, Bounds is just the full image rectangle
var rect = bounds ?? Bounds;
var white = Color.White.ToArgb();
var black = Color.Black.ToArgb();
// scan all x
for (var x = rect.Left; x < rect.Right; x++)
{
var pX = scan0Ptr + x;
// scan all y
for (var y = rect.Top; y < rect.Bottom; y++)
{
if (*(pX + y * stride ) != white)
{
// this will turn it to monochrome
// so add your threshold here, ie some more for loops
//*(pX + y * Stride) = black;
}
}
}
}
// unlock the bitmap
bitmap.UnlockBits(_bitmapData);
To parallel'ize it
You could use something like this to break your image up into smaller regions
public static List<Rectangle> GetSubRects(this Rectangle source, int size)
{
var rects = new List<Rectangle>();
for (var x = 0; x < size; x++)
{
var width = Convert.ToInt32(Math.Floor(source.Width / (double)size));
var xCal = 0;
if (x == size - 1)
{
xCal = source.Width - (width * size);
}
for (var y = 0; y < size; y++)
{
var height = Convert.ToInt32(Math.Floor(source.Height / (double)size));
var yCal = 0;
if (y == size - 1)
{
yCal = source.Height - (height * size) ;
}
rects.Add(new Rectangle(width * x, height * y, width+ xCal, height + yCal));
}
}
return rects;
}
And this
private static void DoWorkload(Rectangle bounds, ParallelOptions options, Action<Rectangle?> workload)
{
if (options == null)
{
workload(null);
}
else
{
var size = 5 // how many rects to work on, ie 5 x 5
Parallel.ForEach(bounds.GetSubRects(size), options, rect => workload(rect));
}
}
Usage
DoWorkload(Bounds, options, Workload);

Win2D Keystone Correction

I'm trying to use Win2D/C# to project an image using a overhead projector and I need to use a Win2D effect to do Keystone Correction (pre-warp the image) as the final step.
Basically I'm drawing a rectangle, then trying to use a Transform3DEffect to warp it before rendering. I can't figure out what Matrix transformation combination to use to get it to work. Doing a full camera projection seems like overkill since I only need warping in one direction (see image below). What transforms should I use?
Using an Image like following, can get you a similar effect.
https://i.stack.imgur.com/5QnEm.png
I am unsure what results in the "bending".
Code for creating the displacement map (with GDI+, because you can set pixels fast).
The LockBitmap you can find here
static void DrawDisplacement(int width, int height, LockBitmap lbmp)
{
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
int roff = (int)((((width >> 1) - x) / (float)(width >> 1)) * ((height - y) / (float)height) * 127);
int goff = 0;
lbmp.SetPixel(x, y, Color.FromArgb(127 - roff, 127 - goff, 0));
}
}
Drawing in Win2D looks something like this, where displacementImage is the loaded file and offscreen, is a 'CanvasRenderTarget' on which I drew the grid.
//Scaling for fitting the image to the content
ICanvasImage scaledDisplacement = new Transform2DEffect
{
BorderMode = EffectBorderMode.Hard,
Source = displacementImage,
TransformMatrix = Matrix3x2.CreateScale((float) (sender.Size.Width / displacementImage.Bounds.Width), (float) (sender.Size.Height / displacementImage.Bounds.Height)),
Sharpness = 1f,
BufferPrecision = CanvasBufferPrecision.Precision32Float,
InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
};
//Blurring, for a better result
ICanvasImage displacement = new GaussianBlurEffect
{
BorderMode = EffectBorderMode.Hard,
Source = scaledDisplacement,
BufferPrecision = CanvasBufferPrecision.Precision32Float,
BlurAmount = 2,
Optimization = EffectOptimization.Quality,
};
ICanvasImage graphicsEffect = new DisplacementMapEffect
{
Source = offscreen,
Displacement = displacement,
XChannelSelect = EffectChannelSelect.Red,
YChannelSelect = EffectChannelSelect.Green,
Amount = 800,//change for more or less displacement
BufferPrecision = CanvasBufferPrecision.Precision32Float,
};

Partially convert to grayscale using AForge?

Converting a bitmap to grayscale is pretty easy with AForge:
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
// first convert to a grey scale image
var filterGreyScale = new Grayscale(0.2125, 0.7154, 0.0721);
me = filterGreyScale.Apply(me);
return me;
}
But I need something more tricky:
Imagine you want to convert everything to grayscale except for a circle in the middle of the bitmap. In other words: a circle in the middle of the given bitmap should keep its original colours.
Let's assume the radius of the circle is 20px, how should I approach this?
This can be accomplished using MaskedFilter with a mask that defines the circled area you describe. As the documentation states
Mask can be specified as .NET's managed Bitmap, as UnmanagedImage or
as byte array. In the case if mask is specified as image, it must be 8
bpp grayscale image. In all case mask size must be the same as size of
the image to process.
So the mask image has to be generated based on the source image's width and height.
I haven't compiled the following code but it should get you on your way. If the circle is always in the same spot, you could generate the image mask outside the method so that it doesn't have to be regenerated each time you apply the filter. Actually you could have the whole MaskedFilter generated outside the method that applies it if nothing changes but the source image.
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
var radius = 20, x = me.Width / 2, y = me.Height / 2;
using (Bitmap maskImage = new Bitmap(me.Width, me.Height, PixelFormat.Format8bppIndexed))
{
using (Graphics g = Graphics.FromImage(maskImage))
using (Brush b = new SolidBrush(ColorTranslator.FromHtml("#00000000")))
g.FillEllipse(b, x, y, radius, radius);
var maskedFilter = new MaskedFilter(new Grayscale(0.2125, 0.7154, 0.0721), maskImage);
return maskedFilter.Apply(me);
}
}
EDIT
The solution for this turned out to be a lot more trickier than I expected. The main problem was that the MaskedFilter doesn't allow the usage of filters that change the images format, which the Grayscale filter does (it changes the source to an 8bpp or 16 bpp image).
The following is the resulting code, which I have tested, with comments added to each part of the ConvertToGrayScale method explaining the logic behind it. The gray-scaled portion of the image has to be converted back to RGB since the Merge filter doesn't support merging two images with different formats.
static class MaskedImage
{
public static void DrawCircle(byte[,] img, int x, int y, int radius, byte val)
{
int west = Math.Max(0, x - radius),
east = Math.Min(x + radius, img.GetLength(1)),
north = Math.Max(0, y - radius),
south = Math.Min(y + radius, img.GetLength(0));
for (int i = north; i < south; i++)
for (int j = west; j < east; j++)
{
int dx = i - y;
int dy = j - x;
if (Math.Sqrt(dx * dx + dy * dy) < radius)
img[i, j] = val;
}
}
public static void Initialize(byte[,] arr, byte val)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = val;
}
public static void Invert(byte[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = (byte)~arr[i, j];
}
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
int radius = 20, x = me.Width / 2, y = me.Height / 2;
// Generate a two-dimensional `byte` array that has the same size as the source image, which will be used as the mask.
byte[,] mask = new byte[me.Height, me.Width];
// Initialize all its elements to the value 0xFF (255 in decimal).
Initialize(mask, 0xFF);
// "Draw" a circle in the `byte` array setting the positions inside the circle with the value 0.
DrawCircle(mask, x, y, radius, 0);
var grayFilter = new Grayscale(0.2125, 0.7154, 0.0721);
var rgbFilter = new GrayscaleToRGB();
var maskFilter = new ApplyMask(mask);
// Apply the `Grayscale` filter to everything outside the circle, convert the resulting image back to RGB
Bitmap img = rgbFilter.Apply(grayFilter.Apply(maskFilter.Apply(me)));
// Invert the mask
Invert(mask);
// Get only the cirle in color from the original image
Bitmap circleImg = new ApplyMask(mask).Apply(me);
// Merge both the grayscaled part of the image and the circle in color in a single one.
return new Merge(img).Apply(circleImg);
}
}

How can I save the location of every single pixel of my image in c#?

I am writing a programme that I need to save the location of every single pixel in my bitmap image in an array and later on in need to for example randomly turn off 300 of black pixels randomly. However I am not sure how to do that. I have written the following code but of course it does not work. Can anyone please tell me the right way of doing that?
The locations of every pixel are constant (every pixel has exactly one x and one y coordinate) so the requirement of save the location of every single pixel is vague.
I guess what you try to do is: Turn 300 pixels in an image black, but save the previous color so you can restore single pixels?
You could try this:
class PixelHelper
{
public Point Coordinate;
public Color PixelColor;
}
PixelHelper[] pixelBackup = new PixelHelper[300];
Random r = new Random();
for (int i = 0; i < 300; i++)
{
int xRandom = r.Next(bmp.Width);
int yRandom = r.Next(bmp.Height);
Color c = bmp.GetPixel(xRandom, yRandom);
PixelHelper[i] = new PixelHelper() { Point = new Point(xRandom, yRandom), PixelColor = c };
}
After that the pixelBackup array contains 300 objects that contain a coordinate and the previous color.
EDIT: I guess from the comment that you want to turn 300 random black pixels white and then save the result as an image again?
Random r = new Random();
int n = 0;
while (n < 300)
{
int xRandom = r.Next(bmp.Width);
int yRandom = r.Next(bmp.Height);
if (bmp.GetPixel(xRandom, yRandom) == Color.Black)
{
bmp.SetPixel(xRandom, yRandom, Color.White);
n++;
}
}
bmp.Save(<filename>);
This turns 300 distinct pixels in your image from black to white. The while loop is used so I can increase n only if a black pixel is hit. If the random coordinate hits a white pixel, another pixel is picked.
Please note that this code loops forever in case there are less than 300 pixels in your image in total.
The following will open an image into memory, and then copy the pixel data into a 2d array. It then randomly converts 300 pixels in the 2d array to black. As an added bonus, it then saves the pixel data back into the bitmap object, and saves the file back to disk.
I edited the code to ensure 300 distinct pixels were selected.
int x = 0, y = 0;
///Get Data
Bitmap myBitmap = new Bitmap("mold.jpg");
Color[,] pixelData = new Color[myBitmap.Width, myBitmap.Height];
for (y = 0; y < myBitmap.Height; y++)
for (x = 0; x < myBitmap.Width; x++)
pixelData[x,y] = myBitmap.GetPixel(x, y);
///Randomly convert 3 pixels to black
Random rand = new Random();
List<Point> Used = new List<Point>();
for (int i = 0; i < 300; i++)
{
x = rand.Next(0, myBitmap.Width);
y = rand.Next(0, myBitmap.Height);
//Ensure we use 300 distinct pixels
while (Used.Contains(new Point(x,y)) || pixelData[x,y] != Color.Black)
{
x = rand.Next(0, myBitmap.Width);
y = rand.Next(0, myBitmap.Height);
}
Used.Add(new Point(x, y)); //Store the pixel we have used
pixelData[x, y] = Color.White;
}
///Save the new image
for (y = 0; y < myBitmap.Height; y++)
for (x = 0; x < myBitmap.Width; x++)
myBitmap.SetPixel(x, y, pixelData[x, y]);
myBitmap.Save("mold2.jpg");

Multiply two images in C# as multiply two layers in Photoshop

I have two images and I want to multiply these two images together in C# as we multiply two layers in Photoshop.
I have found the method by which the layers are multiplied in photoshop or any other application.
Following is the formula that I have found on GIMP documentation. It says that
E=(M*I)/255
where M and I are the color component(R,G,B) values of the two layers. We have to apply this to every color component. E will be the resultant value for that color component.
If the color component values are >255 then it should be set to white i.e. 255 and if it is <0 then it should be set as Black i.e. 0
Here I have a suggestion - I didn't test it, so sorry for any errors - I'm also assuming that both images have the same size and are greylevel.
Basically I'm multiplying the image A for the relative pixel percentage of image B.
You can try different formulas like:
int result = ptrB[0] * ( (ptrA[0] / 255) + 1);
or
int result = (ptrB[0] * ptrA[0]) / 255;
Never forget to check for overflow (above 255)
public void Multiply(Bitmap srcA, Bitmap srcB, Rectangle roi)
{
BitmapData dataA = SetImageToProcess(srcA, roi);
BitmapData dataB = SetImageToProcess(srcB, roi);
int width = dataA.Width;
int height = dataA.Height;
int offset = dataA.Stride - width;
unsafe
{
byte* ptrA = (byte*)dataA.Scan0;
byte* ptrB = (byte*)dataB.Scan0;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x, ++ptrA, ++ptrB)
{
int result = ptrA[0] * ( (ptrB[0] / 255) + 1);
ptrA[0] = result > 255 ? 255 : (byte)result;
}
ptrA += offset;
ptrB += offset;
}
}
srcA.UnlockBits(dataA);
srcB.UnlockBits(dataB);
}
static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
{
if (image != null)
return image.LockBits(
roi,
ImageLockMode.ReadWrite,
image.PixelFormat);
return null;
}

Categories