ProgrammingCuber ProgrammingCuber - 2 months ago 19
Java Question

Sort colors by coordinates of a rectangle

I am working on a program where I take a photo and find the squares in that photo then determine their RGB values. I am able to get this information successfully but when I find the RGB values and the coordinates where I got them from they come in the order of the contours I found them. My question is how can I take the colors and sort them in a way that I read it from the top left row to the bottom row like a book. Each color has the rectangles coordinates it was taken from, I want to use that to help me order them.

I have an image showing what I want to order.

image I want to order

When I get the RGB values from the images I print out the color and the coordinates. Here is what it looks like (Notice how they aren't ordered):

279,271
R: 106 G: 255 B: 183

188,270
R: 107 G: 255 B: 180

371,267
R: 102 G: 255 B: 169

274,178
R: 75 G: 255 B: 99

185,179
R: 84 G: 255 B: 103

369,175
R: 89 G: 255 B: 105

277,88
R: 96 G: 255 B: 103

184,85
R: 101 G: 255 B: 110

370,84
R: 124 G: 255 B: 126


My code that I use to get them is here:

I just don't know how I could take the colors and store them sorted.

private void getColors(Mat img2read , Rect roi){
int rAvg = 0 , bAvg = 0, gAvg = 0;
int rSum = 0, bSum = 0, gSum = 0;

img2read = new Mat(img2read, roi); //image size of rect (saved contour size and coordinates)
img2read.convertTo(img2read, CvType.CV_8UC1); //convert to workable type for getting RGB data

int totalBytes = (int) (img2read.total() * img2read.channels());
byte buff[] = new byte[totalBytes];
int channels = img2read.channels();

img2read.get(0,0,buff);
int stride = channels * img2read.width();
for(int i = 0; i < img2read.height();i++){
for(int x = 0; x < stride; x+= channels){
int r = unsignedToBytes(buff[(i * stride) + x]);
int g = unsignedToBytes(buff[(i * stride)+ x + 1]);
int b = unsignedToBytes(buff[(i * stride)+ x + 2]);

rSum += r;
gSum += g;
bSum += b;

}
}
float[] hsv = new float[3];

rAvg = (int) (rSum / img2read.total());
gAvg = (int) (gSum / img2read.total());
bAvg = (int) (bSum / img2read.total());


Color.RGBtoHSB(rAvg, gAvg, bAvg, hsv);

hsv[2] = 1; //Set to max value

int rgb = Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]);

Color brightenedColor = new Color(rgb);
System.out.println();
System.out.println(roi.x + "," + roi.y);
System.out.printf("R: %s G: %s B: %s \n", brightenedColor.getRed(), brightenedColor.getGreen(), brightenedColor.getBlue());
Color colorToAdd = new Color(brightenedColor.getRed(), brightenedColor.getGreen(), brightenedColor.getBlue());
Point coords = new Point(roi.x, roi.y);
//colorCoords.put(coords, colorToAdd);
/*counter++;
if(counter == 9){
sortColors(colorCoords);
}*/
}

int counter = 0;

Map <Point, Color> colorCoords = new TreeMap<>();
Color[] colorArray = new Color[54];
int currentIndex = 0;

//Not really sure what I'm doing here but I don't think it is right.
private void sortColors(Map<Point, Color> colorCoords){

for(int i = 0; i < colorCoords.size();i++){
// colorCoords.get(key)
}

}

Answer Source

Put those values in to an object, and store them into a List. To sort, you could add a static Comparator field, or make it implement Comparable something like this:

public class Block implement Comparable<Block> {
   public int x, y;
   public Color color;
   @Override
   public int compareTo(Block other) {
      if (null==other) throw new NullPointerException();
      // implement the logic, like:
      // this will compare X, unless difference in Y is more than EG 10
      return Math.abs(y - other.y) > 10 ? y - other.y : x - other.x;
   }
   public Color getColor() { return color; }
}

public List<Block> blocks = new ArrayList<>();

To sort you can use

Collections.sort(blocks);

Edited the above comparison to make to your needs...


With Java8's streams it should be doable to get a sorted array of Colors.

But i am not a streams expert. Try something like this:

 Color[] colors = blocks.stream().sorted().map(Block::getColor).toArray();

For this to work Block.getColor() method is needed, see above.