Simply3DScan
TransformImage.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Drawing;
4 using System.Linq;
5 using AForge;
6 using AForge.Imaging;
7 using AForge.Imaging.Filters;
8 using Configuration;
9 using SharedObjects;
10 using Image = System.Drawing.Image;
12 
13 namespace Simple3DScan
14 {
18  internal static class TransformImage
19  {
27  internal static Bitmap CreateGrayImageOfLaser(Bitmap image, Filter filter)
28  {
29  Bitmap filteredImage = (Bitmap) image.Clone();
30 
31  if (filter.UseHslFilter)
32  {
33  HSLFiltering hslFilter = new HSLFiltering
34  {
35  Saturation = new Range(filter.SaturationMin, filter.SaturationMax),
36  Luminance = new Range(filter.LuminanceMin, filter.LuminanceMax),
37  Hue = new IntRange(filter.HueMin, filter.HueMax)
38  };
39  hslFilter.ApplyInPlace(filteredImage);
40  }
41 
42  if (filter.UseColorFilter)
43  {
44  ColorFiltering colorFilter = new ColorFiltering
45  {
46  Red = new IntRange(filter.FilterRedMin, filter.FilterRedMax),
47  Blue = new IntRange(filter.FilterBlueMin, filter.FilterBlueMax),
48  Green = new IntRange(filter.FilterGreenMin, filter.FilterGreenMax)
49  };
50 
51  colorFilter.ApplyInPlace(filteredImage);
52  }
53 
54  IFilter grayFilter;
55 
56  switch (filter.GrayFilter)
57  {
58  case GrayFilter.BT709:
59  //RGB image to 8bpp gray scale
60  grayFilter = Grayscale.CommonAlgorithms.BT709;
61  break;
62  case GrayFilter.RMY:
63  grayFilter = Grayscale.CommonAlgorithms.RMY;
64  break;
65  case GrayFilter.Y:
66  grayFilter = Grayscale.CommonAlgorithms.Y;
67  break;
68  default:
69  //RGB image to 8bpp gray scale
70  grayFilter = Grayscale.CommonAlgorithms.BT709;
71  break;
72  }
73 
74  Bitmap grayImage = grayFilter.Apply(filteredImage);
75 
76  if (filter.UseThresholdFilter)
77  {
78  Threshold thresholdFilter = new Threshold(filter.Threshold);
79  thresholdFilter.ApplyInPlace(grayImage);
80  }
81 
82  if (filter.UseErosionFilter)
83  {
84  //erosion filter to filter out small unwanted pixels
85  Erosion3x3 errossionFilter = new Erosion3x3();
86  errossionFilter.ApplyInPlace(grayImage);
87  }
88 
89  if (filter.UseDiletationFilter)
90  {
91  //dilation filter
92  Dilatation3x3 dilatationFilter = new Dilatation3x3();
93  dilatationFilter.ApplyInPlace(grayImage);
94  }
95 
96  return grayImage;
97  }
98 
107  //[Log]
108  //[LogException]
109  //public static Bitmap CreateGrayImageOfLaser(Bitmap image)
110  //{
111  // HSLFiltering hslFilter = new HSLFiltering
112  // {
113  // Saturation = new Range(0.05f, 1f),
114  // Luminance = new Range(0.05f, 1f),
115  // Hue = new IntRange(280, 340)
116  // };
117 
118  // ColorFiltering colorFilter = new ColorFiltering
119  // {
120  // Red = new IntRange(252, 255),
121  // Blue = new IntRange(0, 255),
122  // Green = new IntRange(0, 255)
123  // };
124 
125  // //RGB image to 8bpp gray scale
126  // IFilter grayFilter = Grayscale.CommonAlgorithms.BT709;
127  // Bitmap grayImage = grayFilter.Apply(colorFilter.Apply(hslFilter.Apply(image)));
128 
129  // //thresholding a image
130  // Threshold thresholdFilter = new Threshold(40);
131  // thresholdFilter.ApplyInPlace(grayImage);
132 
133  // //erosion filter to filter out small unwanted pixels
134  // Erosion3x3 errossionFilter = new Erosion3x3();
135  // errossionFilter.ApplyInPlace(grayImage);
136 
137 
138  // //dilation filter
139  // Dilatation3x3 dilatationFilter = new Dilatation3x3();
140  // dilatationFilter.ApplyInPlace(grayImage);
141  // return grayImage;
142  //}
143 
149  [Log]
150  [LogException]
151  public static Blob[] GetBlobsFromGrayScaleBitmap(Bitmap bitmap)
152  {
153  BlobCounter blobCounter = new BlobCounter { ObjectsOrder = ObjectsOrder.YX };//MaxWidth = 10, ObjectsOrder = ObjectsOrder.YX };
154  blobCounter.ProcessImage(bitmap);
155  Blob[] blobs = blobCounter.GetObjectsInformation();
156  return blobs;
157  }
158 
164  [Log]
165  [LogException]
166  public static Blob GetLargestBlob(IEnumerable<Blob> blobs)
167  {
168  int maxSize = 0;
169  Blob maxObject = new Blob(0, new Rectangle(0, 0, 0, 0));
170  foreach (Blob blob in blobs)
171  {
172  int blobSize = blob.Rectangle.Width * blob.Rectangle.Height;
173  if (blobSize <= maxSize)
174  continue;
175 
176  maxSize = blobSize;
177  maxObject = blob;
178  }
179 
180  return maxObject;
181  }
182 
189  [Log]
190  [LogException]
191  public static float GetLaserLinePosition(Image laserImage, Filter filter)
192  {
193  Image grayImage = CreateGrayImageOfLaser((Bitmap)laserImage, filter);
194  Image skeletonImage = SimpleSkeletonizationImage((Bitmap)grayImage);
195  Blob[] blobs = GetBlobsFromGrayScaleBitmap((Bitmap)skeletonImage);
196  return GetLaserLinePosition(blobs);
197  }
198 
204  [Log]
205  [LogException]
206  public static float GetLaserLinePosition(Image grayImage)
207  {
208  Image skeletonImage = SimpleSkeletonizationImage((Bitmap)grayImage);
209  Blob[] blobs = GetBlobsFromGrayScaleBitmap((Bitmap)skeletonImage);
210  return GetLaserLinePosition(blobs);
211  }
212 
218  [Log]
219  [LogException]
220  public static float GetLaserLinePosition(Blob[] blobs)
221  {
222  // take laserlight from top 100 pixels and ignore lasers outside a valid range.
223  List<Blob> blobList =
224  blobs.Where(
225  blob => blob.CenterOfGravity.Y < 100)// && blob.CenterOfGravity.X > 100 && blob.CenterOfGravity.X < 800)
226  .ToList(); // also var blobList = ... would be possible. .ToList() prevents lazy evaluation and evaluates linq directly.
227  if (blobList.Count < 1)
228  blobList =
229  blobs.Where(
230  blob => blob.Rectangle.Y < 100)// && blob.CenterOfGravity.X > 100 && blob.CenterOfGravity.X < 800)
231  .ToList();
232  return blobList.Sum(blob => blob.CenterOfGravity.X) / blobList.Count;
233  }
234 
247  [Log]
248  [LogException]
249  internal static List<Blob> ClearBlobs(Blob[] blobs, float laserLineX)
250  {
251  const float safetyFrame = 10f; // pixels directly near laser are seen as laserline
252 
253  return
254  blobs.Where(
255  blob =>
256  blob.CenterOfGravity.X > laserLineX + safetyFrame &&
257  // blob.CenterOfGravity.X > 100 && blob.CenterOfGravity.X < 950 &&
258  blob.CenterOfGravity.Y > 100 && blob.CenterOfGravity.Y < 860)
259  .ToList();
260  }
261 
267  [Log]
268  [LogException]
269  public static Bitmap SimpleSkeletonizationImage(Bitmap bitmap)
270  {
271  SimpleSkeletonization filter = new SimpleSkeletonization();
272  return ReturnFilteredImage(bitmap, filter);
273  }
274 
280  [Log]
281  [LogException]
282  public static Bitmap CannyEdgeImage(Bitmap bitmap)
283  {
284  CannyEdgeDetector filter = new CannyEdgeDetector();
285  return ReturnFilteredImage(bitmap, filter);
286  }
287 
293  [Log]
294  [LogException]
295  public static Bitmap HomogenityEdgeDetectorImage(Bitmap bitmap)
296  {
297  HomogenityEdgeDetector filter = new HomogenityEdgeDetector();
298  return ReturnFilteredImage(bitmap, filter);
299  }
300 
306  [Log]
307  [LogException]
308  public static Bitmap SobelEdgeDetectorImage(Bitmap bitmap)
309  {
310  SobelEdgeDetector filter = new SobelEdgeDetector();
311  return ReturnFilteredImage(bitmap, filter);
312  }
313 
314  [LogException]
315  private static Bitmap ReturnFilteredImage(Bitmap bitmap, IInPlaceFilter filter)
316  {
317  Bitmap returnImage = (Bitmap)bitmap.Clone();
318  filter.ApplyInPlace(returnImage);
319  return returnImage;
320  }
321 
331  [Log]
332  [LogException]
333  public static List<ColorPoint3D> TransformStepImage(Image skeletonImage, Image colorImage, int step, bool microSteps, Information configuration)
334  {
335 
336  List<ColorPoint3D> points = new List<ColorPoint3D>();
337  Blob[] blobs = GetBlobsFromGrayScaleBitmap((Bitmap)skeletonImage);
338  float laserLineX = GetLaserLinePosition(blobs);
339 
340  ColorPoint3D worldCameraSourcePosition = new ColorPoint3D(configuration.Camera.WorldCoordinates.X,
341  configuration.Camera.WorldCoordinates.Y,
342  configuration.Camera.WorldCoordinates.Z);
343 
344  ColorPoint3D worldLaserSourcePosition = new ColorPoint3D(configuration.Laser.WorldCoordinates.X,
345  configuration.Laser.WorldCoordinates.Y,
346  configuration.Laser.WorldCoordinates.Z);
347 
348  ColorPoint3D worldLaserLinePosition = ConvertCameraPixelToWorldPoint(new ColorPoint3D(laserLineX, 0, 0), configuration);
349 
350  Line laserline = new Line(worldLaserSourcePosition, worldLaserLinePosition); // should stay the same
351 
352  // filter irrelevant blobs
353  List<Blob> relevantBlobs = ClearBlobs(blobs, laserLineX);
354 
355  foreach (Blob blob in relevantBlobs)
356  {
357  ColorPoint3D worldPoint = ComputeDepthByIntersection(blob, worldCameraSourcePosition, laserline, configuration);
358  worldPoint.Y = ScaleY(worldPoint, worldCameraSourcePosition);
359 
360  if (!AdjustPointToTurnTable(step, ref worldPoint, microSteps, configuration)) // adjust & test wether point is over turntable
361  continue;
362  worldPoint.Color = colorImage != null
363  ? ((Bitmap) colorImage).GetPixel((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y)
364  : Color.White;
365  points.Add(worldPoint);
366  }
367 
368  return points;
369  }
370 
377  [Log]
378  [LogException]
379  internal static ColorPoint3D ConvertWorldPointToCameraPixel(this ColorPoint3D worldPoint, Information configuration)
380  {
381  ColorPoint3D origin = new ColorPoint3D(configuration.Camera.Width / 2.0f,
382  configuration.Camera.Height * configuration.VisibleLimit.PercentFromTop / 100,
383  0);
384 
385  return new ColorPoint3D(worldPoint.X * configuration.Camera.Width / configuration.Camera.WorldWidth + origin.X,
386  -worldPoint.Y * configuration.Camera.Height / configuration.Camera.WorldHeight + origin.Y,
387  0);
388  }
389 
396  [Log]
397  [LogException]
398  internal static ColorPoint3D ConvertCameraPixelToWorldPoint(this ColorPoint3D cameraPoint, Information configuration)
399  {
400  ColorPoint3D worldPoint = new ColorPoint3D(cameraPoint.X - configuration.Camera.Width / 2.0f,
401  cameraPoint.Y - configuration.Camera.Height * configuration.VisibleLimit.PercentFromTop / 100,
402  0);
403 
404  return new ColorPoint3D(worldPoint.X * configuration.Camera.WorldWidth / configuration.Camera.Width,
405  -worldPoint.Y * configuration.Camera.WorldHeight / configuration.Camera.Height,
406  0);
407  }
408 
409  [LogException]
410  private static ColorPoint3D ComputeDepthByIntersection(Blob blob, ColorPoint3D worldCameraSourcePosition, Line laserline, Information configuration)
411  {
412  // convert point to world
413  ColorPoint3D worldPoint2D = ConvertCameraPixelToWorldPoint(new ColorPoint3D(blob.CenterOfGravity.X, blob.CenterOfGravity.Y, 0), configuration);
414  Line viewLine = new Line(worldCameraSourcePosition, worldPoint2D);
415  // compute intersection
416  ColorPoint3D worldPoint = viewLine.Intersection(laserline);
417  worldPoint.Y = worldPoint2D.Y;
418  return worldPoint;
419  }
420 
421  [LogException]
422  private static double ScaleY(ColorPoint3D worldPoint, ColorPoint3D worldCameraSourcePosition)
423  {
424  double y = worldPoint.Y - worldCameraSourcePosition.Y;
425  y *= (worldCameraSourcePosition.Z - worldPoint.Z) / worldCameraSourcePosition.Z;
426  y += worldCameraSourcePosition.Y;
427  return y;
428  }
429 
430  [LogException]
431  private static bool AdjustPointToTurnTable(int step, ref ColorPoint3D worldPoint, bool microSteps, Information configuration)
432  {
433  double turntableDegree = GetTurntableDegree(step, microSteps, configuration);
434 
435  worldPoint.Z -= configuration.Turntable.WorldCoordinates.Z;
436 
437  double alpha = Math.Atan(worldPoint.Z/worldPoint.X) + turntableDegree*(Math.PI/180.0f);
438  double hypo = Math.Sqrt(worldPoint.X*worldPoint.X + worldPoint.Z*worldPoint.Z);
439 
440  if (worldPoint.X < 0)
441  if (worldPoint.Z < 0)
442  alpha += Math.PI;
443  else
444  alpha -= Math.PI;
445 
446  worldPoint.Z = Math.Sin(alpha) * hypo;
447  worldPoint.X = Math.Cos(alpha) * hypo;
448 
449  double turntableSize = configuration.Turntable.Radius - (float) configuration.Turntable.SafetyDistance/10f;
450  double tablePos = Math.Sqrt(worldPoint.Z*worldPoint.Z + worldPoint.X*worldPoint.X);
451 
452  return worldPoint.Y > 0.5 && hypo < 7 && tablePos < turntableSize;
453  }
454 
455  [LogException]
456  private static double GetTurntableDegree(int step, bool microSteps, Information configuration)
457  {
458  int numberOfTotalSteps = microSteps
459  ? configuration.Motor.MicroSteps *
460  configuration.Motor.Steps
461  : configuration.Motor.Steps;
462 
463  double turntableDegree = 360.0f - (double)step * 360.0f / (double)numberOfTotalSteps;
464  return turntableDegree;
465  }
466  }
467 }
float WorldHeight
Gets the height of the world.
Definition: Camera.cs:60
int FilterGreenMax
Gets or sets the filter green maximum.
Definition: Filter.cs:88
Coordinates WorldCoordinates
Gets or sets the world coordinates.
Definition: Laser.cs:16
static Blob GetLargestBlob(IEnumerable< Blob > blobs)
Gets the largest BLOB.
static Bitmap HomogenityEdgeDetectorImage(Bitmap bitmap)
Homogenity edge detector for image.
float LuminanceMin
Gets or sets the luminance minimum.
Definition: Filter.cs:32
float Y
Gets or sets the y.
Definition: Coordinates.cs:21
Camera Camera
Gets or sets the camera.
Definition: Information.cs:24
GrayFilter GrayFilter
Gets or sets the gray filter.
Definition: Filter.cs:109
int Threshold
Gets or sets the threshold.
Definition: Filter.cs:123
static float GetLaserLinePosition(Image laserImage, Filter filter)
Gets the laser line position.
SharedObjects.Line Line
Class holding the configuration information
Definition: Information.cs:8
double Z
Gets or sets the z.
Definition: ColorPoint3D.cs:30
bool UseHslFilter
Gets or sets a value indicating whether [use HSL filter].
Definition: Filter.cs:11
bool UseThresholdFilter
Gets or sets a value indicating whether [use threshold filter].
Definition: Filter.cs:116
double Y
Gets or sets the y.
Definition: ColorPoint3D.cs:23
Laser Laser
Gets or sets the laser.
Definition: Information.cs:17
static Bitmap CreateGrayImageOfLaser(Bitmap image, Filter filter)
Creates the gray image of laser.
static ColorPoint3D ConvertCameraPixelToWorldPoint(this ColorPoint3D cameraPoint, Information configuration)
Converts the camera pixel to world point.
static Bitmap SimpleSkeletonizationImage(Bitmap bitmap)
Skeletonization for image.
Turntable Turntable
Gets or sets the turntable.
Definition: Information.cs:31
int SafetyDistance
Gets or sets the safety distance.
Definition: Turntable.cs:32
int FilterGreenMin
Gets or sets the filter green minimum.
Definition: Filter.cs:81
float WorldWidth
Gets or sets the width of the world.
Definition: Camera.cs:53
int FilterRedMin
Gets or sets the filter red minimum.
Definition: Filter.cs:67
static float GetLaserLinePosition(Image grayImage)
Gets the laser line position.
static List< ColorPoint3D > TransformStepImage(Image skeletonImage, Image colorImage, int step, bool microSteps, Information configuration)
Transforms the step image.
double PercentFromTop
Gets or sets the percent from top.
Definition: Line.cs:33
double X
Gets or sets the x.
Definition: ColorPoint3D.cs:16
static List< Blob > ClearBlobs(Blob[] blobs, float laserLineX)
Clears the blobs. Top < 100 in y is seen as only needed for laserline. Left > 100 in x are seen as ou...
Represents a simple line
Definition: Line.cs:6
int FilterBlueMax
Gets or sets the filter blue maximum.
Definition: Filter.cs:102
static float GetLaserLinePosition(Blob[] blobs)
Gets the laser line position.
int Height
Gets or sets the height of the frame.
Definition: Camera.cs:46
float Z
Gets or sets the z.
Definition: Coordinates.cs:28
float LuminanceMax
Gets or sets the luminance maximum.
Definition: Filter.cs:39
float X
Gets or sets the x.
Definition: Coordinates.cs:14
int FilterRedMax
Gets or sets the filter red maximum.
Definition: Filter.cs:74
Does transformations of an image
int MicroSteps
Gets or sets the number of micro steps.
Definition: Motor.cs:24
Coordinates WorldCoordinates
Gets or sets the world coordinates.
Definition: Turntable.cs:15
int HueMax
Gets or sets the hue maximum.
Definition: Filter.cs:53
Line VisibleLimit
Gets or sets the visible limit.
Definition: Information.cs:38
static Bitmap CannyEdgeImage(Bitmap bitmap)
CannyEdge filters the image.
static ColorPoint3D ConvertWorldPointToCameraPixel(this ColorPoint3D worldPoint, Information configuration)
Converts the world point to camera pixel.
bool UseDiletationFilter
Gets or sets a value indicating whether [use diletation filter].
Definition: Filter.cs:137
Coordinates WorldCoordinates
Gets or sets the world coordinates.
Definition: Camera.cs:30
bool UseColorFilter
Gets or sets a value indicating whether [use color filter].
Definition: Filter.cs:60
int FilterBlueMin
Gets or sets the filter blue minimum.
Definition: Filter.cs:95
int Width
Gets or sets the width.
Definition: Camera.cs:38
int HueMin
Gets or sets the hue minimum.
Definition: Filter.cs:46
float SaturationMax
Gets or sets the saturation maximum.
Definition: Filter.cs:25
float SaturationMin
Gets or sets the saturation minimum.
Definition: Filter.cs:18
Motor Motor
Gets or sets the motor.
Definition: Information.cs:52
static Blob[] GetBlobsFromGrayScaleBitmap(Bitmap bitmap)
Gets the blobs from gray scale bitmap.
int Steps
Gets or sets the number of steps.
Definition: Motor.cs:16
static Bitmap SobelEdgeDetectorImage(Bitmap bitmap)
Sobels edge detector for image.
double Radius
Gets or sets the radius.
Definition: Turntable.cs:23
bool UseErosionFilter
Gets or sets a value indicating whether [use erosion filter].
Definition: Filter.cs:130