Organize centroid points into Array Structure

Hello Folks,
I have centroid points in the form of an array of points final_result[1]. I want to organize the centroid points of the array in such a way that it maintains the row and column of the original image. Any help will be highly appreciated.
Link of my Notebook: https://observablehq.com/d/6bae8fed0adcd8b1
Thanks

1 Like

I don’t see an image in your notebook?

And are you asking about sorting your array, or about turning your 1D array into a 2D array?

@mootari Thank you very much for your response. The image (circle_new.jpg) is attached to the notebook. Observable is currently taking unexpectedly a long time to load. I noticed similar problems with other notebooks and contacted Observable customer service about it. Hope they will solve this issue soon.

I’ve attached the image (circle_new.jpg) as well as the output of the final result[1] for your reference. I’m currently getting the final result[1] output as a 1D pointer array. But I want a 2D array with centroid points organized similarly to the original image. I tried sorting and slicing, but that didn’t help because the circle is missing in some rows and columns of original image.

Current output of

final_result[1] = Array (21) [
                                                       0: Point {x:, y: }
                                                      1: Point {x:, y: }
                                                      2: Point {x:, y: }
                                                      3: Point {x:, y: }
                                                      ...
                                                      21: Point {x:, y: }
]

My goal is like below:

final result[1] = Array (21) [
0: Array (5) [Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }]
1: Array(5)  [Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }]
2:  Array(5) [Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }]
3: Array(2) [Point {x:, y: }, Point {x:, y: }]
4: Array(4) [Point {x:, y: }, Point {x:, y: }, Point {x:, y: }, Point {x:, y: }]
]

I have several images like this. I’m trying to figure out how to make it work for all images.

circle_new

Thank you once again for your help!

@joyaircel2016 The notebook loads fine, but your code only shows the image after clicking on the headline:

The spinning throbbers that you see in the editors indicate that the cells are still computing, or in this case awaiting input.

@mootari Yes, I understand that cells are still computing. Previously, I worked on the same notebook with almost zero waiting time. This notebook is very simple. I am facing computing time issues with other notebooks as well.

It’s a tricky problem, and what solution is viable really depends on how much those points are shifted diagonally. At least for this image though the following solution should work:

{
  // Don't mutate original array.
  const points = final_result[1].slice();
  // Sort vertically.
  points.sort((a, b) => a.y - b.y);
  // Calculate vertical jumps.
  const yDeltas = points.map((p, i, arr) => p.y - arr[i - 1]?.y || 0);
  // Find safe threshold at which to start a new row.
  const threshold = d3.bin().thresholds(2)(yDeltas).reduce((max, bin) => Math.max(max, bin.x0), 0);
  // Split points into rows.
  const rows = yDeltas.reduce((rows, d, i) => {
    // Difference exceeds threshold, create a new row.
    if(d >= threshold) rows.push([]);
    // Add point to most recent row.
    rows[rows.length - 1].push(points[i]);
    return rows;
  }, [[]]);
  // Sort by x in each row.
  rows.forEach(arr => arr.sort((a, b) => a.x - b.x));
  return rows;
}
1 Like

@mootari Thank you, it worked for this image. I’m experimenting with some changes to see if it works for other images as well.

The thresholding idea is fantastic, but the same condition does not apply to all images. Hence, I’m thinking of slicing based on the length of each row. Is it possible to define the length of each row and slice the array according to that? Thanks so much for your kind help.

Something like below:
row_length = [5, 5, 5, 2, 4];
…
…

for (var i=0; i<=row_length.length(); i++) {
if(d >= row_length[i]) rows.push([]);
    // Add point to most recent row.
    rows[rows.length - 1].push(points[i]);
    return rows;
}

I’m afraid I don’t understand what problem you’re trying to solve.

I am thinking if we can define the length of each row and slice the 1D array accordingly. I have posted this issue in StackOverflow:

Issue resolved. Thanks a lot!. Details are in StackOverflow: