I did some debugging and found the problem.
The thing is that your white is not completely white and even with the threshold sometimes spots are left aside.
That’s I added the smoothWhiteBackground()
that was getting rid of small gaps by checking if it was surrounded by close pixels. The biggest problem was with the black edge on the top between the 3 upper cards on the right.
To solve your problem I changed the distance to 5 pixels (instead of 2 pixels) and then called it 2 times at the end of the removeWhiteBackground()
function instead of 1 time. It works but that’s not enough to ensure that everything will go correctly.
It will crash if you have more than 8 zones identified. What you should do is, after you remove the white background (just the main part actually), use another flood algorithm to get the size of each “air pocket” that were created. Then if the size is less than x pixels, you get rid of them, they are part of the background.
Anyway, there is the “improved” version:
PImage originalPic; // The pic with your 8 pictures
int w, h, wh; // Width and height of originalPic and product of the 2 variables
boolean[] pixelsUsed; // Store the pixels that we have already used for the background or a cart
ArrayList<Integer> queuedPixels; // Store the pixels that will need to be analyzed
boolean[] pixelsAlreadySelected; // Store the pixels that have been analyzed and the one that are waiting to be analyzed in the queudPixels array
PImage[] cards; // Store the cards
int imgToShow;
int xPos, yPos;
void setup() {
//fullScreen();
background(20);
// Init
originalPic = loadImage("ImgTest2.jpg");
w = originalPic.width;
h = originalPic.height;
wh = w * h;
cards = new PImage[8];
imgToShow = 0;
queuedPixels = new ArrayList<Integer>();
pixelsUsed = new boolean[wh];
pixelsAlreadySelected = new boolean[wh];
xPos = 0;
yPos = 0;
// Initializing the pixelAnalyzed array with false values
for (int i = 0; i < pixelsUsed.length; i++) {
pixelsUsed[i] = false;
}
// Initializing the pixelsAlreadySelected array with false values
for (int i = 0; i < pixelsAlreadySelected.length; i++) {
pixelsAlreadySelected[i] = false;
}
// Do the magic
removeWhiteBackground();
//originalPic.loadPixels();
//for (int i = 0; i < wh; i++) {
// if (pixelsUsed[i]) {
// originalPic.pixels[i] = color(255, 0, 0);
// } else {
// originalPic.pixels[i] = color(255, 255, 255);
// }
//}
//originalPic.updatePixels();
for (int i = 0; i < 8; i++) {
getCard(i);
cards[i].save("card_" + i + ".jpg");
}
//image(cards[imgToShow], 0, 0);
println("Done");
}
void removeWhiteBackground() {
// Getting rid of the white background (assuming the top right pixels is white)
// Init
queuedPixels.add(0);
pixelsAlreadySelected[0] = true;
while (queuedPixels.size() > 0) { // While there is some pixels to analyzed
int idx = queuedPixels.get(0); // Get the index of the next pixel to analyzed
if (colorDistance2(originalPic.pixels[idx], color(255, 255, 255)) <= 25) { // If that pixel is white
pixelsUsed[idx] = true; // We now have used that pixel for the background
//if (idx == 3547934 || idx == 3551439 || idx == 3554942 || idx == 3551437) {
// println(idx);
// }
if (idx - w >= 0 && pixelsAlreadySelected[idx - w] == false) { // Spread to the top
pixelsAlreadySelected[idx - w] = true;
queuedPixels.add(idx - w);
}
if ((idx % w) != (w - 1) && idx + 1 < wh && pixelsAlreadySelected[idx + 1] == false) { // Spread to the right
pixelsAlreadySelected[idx + 1] = true;
queuedPixels.add(idx + 1);
}
if (idx + w < wh && pixelsAlreadySelected[idx + w] == false) { // Spread to the bottom
pixelsAlreadySelected[idx + w] = true;
queuedPixels.add(idx + w);
}
if ((idx % w) != 0 && idx - 1 >= 0 && pixelsAlreadySelected[idx - 1] == false) { // Spread to the left
pixelsAlreadySelected[idx - 1] = true;
queuedPixels.add(idx -1);
}
}
queuedPixels.remove(0); // Don't forget to remove the pixel from the queue
}
SmoothWhiteBackground();
SmoothWhiteBackground();
}
// Used to avoid some issues with edge pixels that are some times black
// Consider a pixel as a background if it has pixels above and under or to the left and to the right (the dist variable set how many pixels to the top rigth bottom left we are checking)
void SmoothWhiteBackground() {
boolean[] pixelsUsedCopy;
int dist = 5;
boolean up, right, bottom, left;
pixelsUsedCopy = new boolean[wh];
arrayCopy(pixelsUsed, pixelsUsedCopy);
for (int i = 0; i < wh; i++) {
if (pixelsUsed[i] == false) {
int x = i % w;
int y = i / w;
up = false;
right= false;
bottom = false;
left = false;
// Up
if (y < dist) {
up = true;
} else {
for (int j = 0; j < dist; j++) {
if (pixelsUsedCopy[i - (j + 1) * w] == true) {
up = true;
}
}
}
// Right
if (x > w - dist - 1) {
right = true;
} else {
for (int j = 0; j < dist; j++) {
if (pixelsUsedCopy[i + j + 1] == true) {
right = true;
}
}
}
// Bottom
if (y > h - dist - 1) {
bottom = true;
} else {
for (int j = 0; j < dist; j++) {
if (pixelsUsedCopy[i + (j + 1) * w] == true) {
bottom = true;
}
}
}
// Up
if (x < dist) {
left = true;
} else {
for (int j = 0; j < dist; j++) {
if (pixelsUsedCopy[i - (j + 1)] == true) {
left = true;
}
}
}
// Update the array
if ((up && bottom) || (left && right)) {
pixelsUsed[i] = true;
//if (i == 3547934 || i == 3551439 || i == 3554942 || i == 3551437) {
// println(i);
//}
}
}
}
}
void getCard(int imgNb) {
//originalPic.loadPixels();
//color col = color(random(255), random(255), random(255));
int xMax, xMin, yMax, yMin; // The boundary of the card
// Init
xMax = 0;
xMin = w;
yMax = 0;
yMin = h;
// Find the first pixel that is not part of the background or that wasn't used for another card
int i = 0;
while (pixelsUsed[i] == true) {
i++;
}
// Reset the queue
queuedPixels.clear();
queuedPixels.add(i);
// Reset pixelsAlreadySelected
for (int j = 0; j < pixelsAlreadySelected.length; j++) {
pixelsAlreadySelected[j] = false;
}
pixelsAlreadySelected[i] = true;
// Get the card
while (queuedPixels.size() > 0) {
int idx = queuedPixels.get(0);
if (pixelsUsed[idx] == false) { // It is part of the card
pixelsUsed[idx] = true;
//originalPic.pixels[idx] = col;
// transform idx in x, y coordinate
int x = idx % w;
int y = idx / w;
// Getting the lower and upper value
if (x > xMax) {
xMax = x;
}
if (x < xMin) {
xMin = x;
}
if (y > yMax) {
yMax = y;
}
if (y < yMin) {
yMin = y;
}
//Spreading
if (idx - w >= 0 && pixelsAlreadySelected[idx - w] == false) { // Spread to the top
pixelsAlreadySelected[idx - w] = true;
queuedPixels.add(idx - w);
}
if ((idx % w) != (w - 1) && idx + 1 < wh && pixelsAlreadySelected[idx + 1] == false) { // Spread to the right
pixelsAlreadySelected[idx + 1] = true;
queuedPixels.add(idx + 1);
}
if (idx + w < wh && pixelsAlreadySelected[idx + w] == false) { // Spread to the bottom
pixelsAlreadySelected[idx + w] = true;
queuedPixels.add(idx + w);
}
if ((idx % w) != 0 && idx - 1 >= 0 && pixelsAlreadySelected[idx - 1] == false) { // Spread to the left
pixelsAlreadySelected[idx - 1] = true;
queuedPixels.add(idx -1);
}
}
queuedPixels.remove(0);
}
cards[imgNb] = originalPic.get(xMin, yMin, xMax - xMin, yMax - yMin);
//originalPic.updatePixels();
}
int colorDistance(color col1, color col2) {
int deltaR = (col1 >> 16 & 0xFF) - (col2 >> 16 & 0xFF);
int deltaG = (col1 >> 8 & 0xFF) - (col2 >> 8 & 0xFF);
int deltaB = (col1 & 0xFF) - (col2 & 0xFF);
return deltaR * deltaR + deltaG * deltaG + deltaB * deltaB;
}
int colorDistance2(color col1, color col2) {
int deltaR = (col1 >> 16 & 0xFF) - (col2 >> 16 & 0xFF);
int deltaG = (col1 >> 8 & 0xFF) - (col2 >> 8 & 0xFF);
int deltaB = (col1 & 0xFF) - (col2 & 0xFF);
return max(abs(deltaR), abs(deltaG), abs(deltaB));
}
void draw() {
//background(20);
//image(originalPic, xPos, yPos, 7008, 4954);
//fill(0);
//noStroke();
//int x = 3551438 % w;
//int y = 3551438 / w;
//ellipse(2 * x + xPos, 2 * y + yPos, 5, 5);
}
void mouseClicked() {
//imgToShow++;
//if (imgToShow > 7) {
// imgToShow = 0;
//}
//background(20);
//image(cards[imgToShow], 0, 0);
}
void keyPressed() {
if (keyCode == 38) {
yPos += 100;
}
if (keyCode == 39) {
xPos -= 100;
}
if (keyCode == 40) {
yPos -= 100;
}
if (keyCode == 37) {
xPos += 100;
}
}