American forum: asked a question - get an answer.
Israeli forum: asked a question - you get a question.
Russian forum: asked a question - they will explain to you what an asshole you are.
byte[] bytes = response.Frame; int stride = bytes.Length / height; byte[,] belong = (byte[,])Array.CreateInstance(typeof(byte), new int[] { 326, 246 }, new int[] { -3, -3 }); int Ythreshold = settings.Ythreshold; for (int y = 0; y < 240; y++) { int offset = stride * y * 2; for (int x = 0; x < 320; x++) { int blu = bytes[offset] + bytes[offset + 3] + bytes[offset + stride] + bytes[offset + stride + 3]; offset++; int grn = bytes[offset] + bytes[offset + 3] + bytes[offset + stride] + bytes[offset + stride + 3]; offset++; int red = bytes[offset] + bytes[offset + 3] + bytes[offset + stride] + bytes[offset + stride + 3]; offset += 4; belong[x, y] = (0.299 / 4 * red + 0.587 / 4 * grn + 0.114 / 4 * blu > Ythreshold ? (byte)1 : (byte)0); } }
private bool[,] filtered = (bool[,])Array.CreateInstance(typeof(bool), new int[] { 326, 246 }, new int[] { -3, -3 }); int medianThreshold = settings.MedianThreshold; for (int x = 0; x < 320; x++) for (int y = 0; y < 240; y++) filtered[x, y] = belong[x - 1, y - 2] + belong[x, y - 2] + belong[x + 1, y - 2] + belong[x - 2, y - 1] + belong[x - 1, y - 1] + belong[x, y - 1] + belong[x + 1, y - 1] + belong[x + 2, y - 1] + belong[x - 2, y * 1] + belong[x - 1, y * 1] + belong[x, y * 1] + belong[x + 1, y * 1] + belong[x + 2, y * 1] + belong[x - 2, y + 1] + belong[x - 1, y + 1] + belong[x, y + 1] + belong[x + 1, y + 1] + belong[x + 2, y + 1] + belong[x - 1, y + 2] + belong[x, y + 2] + belong[x + 1, y + 2] > medianThreshold;
belong
and filtered
arrays - [-3..322, -3..242] - was chosen on purpose with “fields” of three pixels on each side in order to work with these arrays without bothering with index checks. int biggest_area = 0; byte area_id = 1, biggest_id = 0; // areas start from 2 Rectangle bounds = new Rectangle(); PointF cg = new PointF(); // center Point[] stack = new Point[320*200]; for (int x = 0; x < 320; x++) for (int y = 0; y < 240; y++) if (filtered[x, y] && belong[x, y] <= 1) { int area = 0, left = 320, top = 240, right = 0, bottom = 0; int sx = 0, sy = 0; ++area_id; // FloodFill int sp = 0; stack[0] = new Point(x, y); while (sp >= 0) { Point next = stack[sp--]; area++; sx += next.X; sy += next.Y; belong[next.X, next.Y] = area_id; if (next.X < left) left = next.X; if (next.X > right) right = next.X; if (next.Y < top) top = next.Y; if (next.Y > bottom) bottom = next.Y; if (filtered[next.X - 1, next.Y] && belong[next.X - 1, next.Y] <= 1) stack[++sp] = new Point(next.X - 1, next.Y); if (filtered[next.X, next.Y - 1] && belong[next.X, next.Y - 1] <= 1) stack[++sp] = new Point(next.X, next.Y - 1); if (filtered[next.X, next.Y + 1] && belong[next.X, next.Y + 1] <= 1) stack[++sp] = new Point(next.X, next.Y + 1); if (filtered[next.X + 1, next.Y] && belong[next.X + 1, next.Y] <= 1) stack[++sp] = new Point(next.X + 1, next.Y); } if (area > biggest_area) { biggest_area = area; biggest_id = area_id; bounds = new Rectangle(left, top, right - left, bottom - top); cg = new PointF((float)sx / area, (float)sy / area); } }
cg
(in the image is a red dot) and borders (the green dot is the center of the bounding box). We can now check how much our white spot looks like a window: the biggest_area
area should be no less than 2000 pixels, the distance between two centers should be no more than 20 pixels, otherwise the figure is too asymmetric for a rectangle. But how further will we determine its orientation? PointF c = new PointF(bounds.Left + (float)bounds.Width / 2, bounds.Top + (float)bounds.Height / 2); int[] hist = new int[400]; for (int i = 0; i < 400; i++) hist[i] = 0; int maxdist = 0; for (int x = bounds.Left; x <= bounds.Right; x++) for (int y = bounds.Top; y <= bounds.Bottom; y++) if (belong[x, y] == biggest_id) { int dist = (int)Math.Sqrt(Sqr(x - cX) + Sqr(y - cY)); hist[dist]++; if (dist > maxdist) maxdist = dist; }
int r1 = 0; // incircle radius for (int x = maxdist; x >= 3; x--) { if (hist[x] > hist[r1]) r1 = x; } int rSample = r1 + 5; int[] voters = new int[4]; Point[] sums = new Point[4]; Point sampleOrg = new Point(Math.Max((int)(cX - rSample), 0), Math.Max((int)(cY - rSample), 0)); Rectangle sample = new Rectangle(sampleOrg, new Size( Math.Min((int)(cX + rSample), 319) - sampleOrg.X, Math.Min((int)(cY + rSample), 239) - sampleOrg.Y)); for (int x = sample.Left; x <= sample.Right; x++) for (int y = sample.Top; y <= sample.Bottom; y++) if (belong[x, y] != biggest_id) { int dist = (int)Math.Sqrt(Sqr(x - cX) + Sqr(y - cY)); if (dist > r1 && dist <= rSample) { int idx = y < cY ? (x < cX ? 1 : 0) : (x < cX ? 2 : 3); voters[idx]++; sums[idx].X += x; sums[idx].Y += y; } } PointF adjusted = new PointF(); int vAbove = voters[0] + voters[1], vBelow = voters[2] + voters[3], vLeft = voters[2] + voters[1], vRight = voters[0] + voters[3], allVoters = vAbove + vBelow; if (allVoters == 0) { // abort: no black pixels found } else { if (vAbove > 0 && vBelow > 0) { // split vertically PointF above = new PointF((float)(sums[0].X + sums[1].X) / vAbove - cX, (float)(sums[0].Y + sums[1].Y) / vAbove - cY), below = new PointF((float)(sums[2].X + sums[3].X) / vBelow - cX, (float)(sums[2].Y + sums[3].Y) / vBelow - cY); double dAbove = Math.Sqrt(above.X * above.X + above.Y * above.Y), dBelow = Math.Sqrt(below.X * below.X + below.Y * below.Y); if (dAbove >= r1 && dAbove <= rSample && dBelow >= r1 && dBelow <= rSample) // the split is valid adjusted = new PointF((above.X * vAbove - below.X * vBelow) / allVoters, (above.Y * vAbove - below.Y * vBelow) / allVoters); } if (adjusted.X == 0 && adjusted.Y == 0 && vLeft > 0 && vRight > 0) { // split horizontally PointF toleft = new PointF((float)(sums[2].X + sums[1].X) / vLeft - cX, (float)(sums[2].Y + sums[1].Y) / vLeft - cY), toright = new PointF((float)(sums[0].X + sums[3].X) / vRight - cX, (float)(sums[0].Y + sums[3].Y) / vRight - cY); double dLeft = Math.Sqrt(toleft.X * toleft.X + toleft.Y * toleft.Y), dRight = Math.Sqrt(toright.X * toright.X + toright.Y * toright.Y); if (dLeft >= r1 && dLeft <= rSample && dRight >= r1 && dRight <= rSample) // the split is valid adjusted = new PointF((toleft.X * vLeft - toright.X * vRight) / allVoters, (toleft.Y * vLeft - toright.Y * vRight) / allVoters); } }
adjusted
point now points from the center of the rectangle along its transverse axis:WindowDetector
in the MRDS service WindowDetector
- in the image and likeness of standard Technologies\Vision\ColorSegment
. My service is tied to the video camera, and for each frame update it sends to subscribers UpdateFoundWindow
with the angle of the window in the frame. The MRDS service that manages the robot binds to the WindowDetector
in the same way as it binds to a barcode scanner, and processes messages from both quite similarly - correcting the course in the first case and the position in the other case.Source: https://habr.com/ru/post/164979/
All Articles