How to convert color data in Processing - processing

My goal is to take each pixel from an input image, sort each pixel based on its value, and then output a reorganized version of the original image according to the sorted array of pixel values. I am fairly certain that I need to use the hex() function in order to format the color data so that Processing can interpret it again, however I'm not getting the desired result. As it is, I am only getting an all black array of pixels when I run my sketch..
size(150, 150);
PImage myImage = loadImage("image.jpg");
image(myImage, 0, 0);
int[] colors = new int[22500];
loadPixels();
for (int i = 0; i < 22500; i++) {
colors[i] = pixels[i];
}
updatePixels();
int x = 1;
int y = 1;
colors = sort(colors);
for (int i = 0; i < 22500; i++) {
color c = colors[i];
fill(c);
rect(x, y, 1, 1);
x = x + 1;
if (x > width-3) {
x = 1;
y = y + 1;
}
}

I think you are overcomplicating things a bit... You can just do this (this is the whole sketch):
PImage myImage = loadImage("image.jpg");
size(myImage.width*2, myImage.height);
myImage.loadPixels();
image(myImage, 0, 0);
myImage.pixels = sort(myImage.pixels);
myImage.updatePixels();
image(myImage, myImage.width, 0);
I have a feeling though that the result will not be exactly what you expect... If you wanted smooth transition between colours the "sort" function will not do as colours are not stored like that. A quick explanation:
Every pixel's colour is a combination of three colours Red Green and Blue (lets leave transparency our for the moment). So an orange with your typical colour selector is 255(R) 204(G) 0(B). These three values are appended next to each other to store the final colour. It easier to understand if you convert them to hex thus: FF(R) CC(G) 00(B). The final value in hex looks like this: FFCC00. If you translate this to decimal again so that you can store it you get 16763904, which is what the pixels[] array is filled with. Well if you sort the pixels[] array as you are trying to do, what you get is essentially an array sorted like this:
R G B
000 000 000
000 000 001 // a bit blue but almost black
000 000 002 // getting bluer
.
.
.
000 000 255 // all blue
000 001 000 // a bit green but almost black
000 001 001 // a bit green and a bit blue
.
.
.
000 255 000 // all green
000 255 001 // all green and a bit blue (going to cyan)
.
.
.
000 255 255 // cyan
001 000 000 // a bit red but almost black
001 001 000 // a bit red and a bit green
.
.
.
255 000 000 // all red
.
.
.
255 255 000 // yellow
.
.
.
255 255 255 // white
The final effect (on a mostly black + cyan image):

Your sketch is missing a noStroke(). You need to add it somewhere before the for loop that's drawing rectangles of side 1, otherwise the borders of the rectangles (that are black) will fill the whole sketch.
Alternatively, substitute
fill(c);
rect(x, y, 1, 1);
with
stroke(c);
rect(x, y, 1, 1);
You can also use a point instead of a rect but then you need to add a noSmooth()
noSmooth();
stroke(c);
point(x, y);
Just for fun, here is a Mondrian picture sorted with your script
unsorted: sorted:
If you are looking for other ways of sorting colors you can write your own methods or use an available library such as http://toxiclibs.org/.

Related

Very unexpected behavior of C++ win32 BitBlt

I noticed when I try to run BitBlt, the resulting data buffer is unexpected in two ways:
It is flipped along the y axis (the origin seems to be bottom left instead of top left)
In each RGBA grouping, the R and B values seem to be switched.
For the first issue, I noticed it when testing with my command prompt; if my command prompt was in the upper left portion of the screen, it would only say it was black when my cursor was in the lower left portion. I had to fix the inversion of the y axis by changing int offset = (y * monitor_width + x) * 4; to int offset = ((monitor_height - 1 - y) * monitor_width + x) * 4; this fixed the pixel location issue because it was showing black where I expected black.
However, the colors were still strong. I tested by trying to get the color of known pixels. I noticed every blue pixel had a very high R value and every red pixel had a very high blue value. That's when I compared with an existing tool I had and found out that the red and blue values seem to be switched in every pixel. At first I thought it was backwards or a byte alignment issue, but I also verified in a clustering of pixels that aren't uniform to make sure it's picking the right position of pixel, and it did perfectly well, just with the colors switched.
Full simplified code below (originally my tool was getting my cursor position and printing the pixel color via hotkey press; this is a simplified version that gets one specific point).
BYTE* my_pixel_data;
HDC hScreenDC = GetDC(GetDesktopWindow());
int BitsPerPixel = GetDeviceCaps(hScreenDC, BITSPIXEL);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int monitor_width = GetSystemMetrics(SM_CXSCREEN);
int monitor_height = GetSystemMetrics(SM_CYSCREEN);
std::cout << std::format("monitor width height: {}, {}\n", monitor_width, monitor_height);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = monitor_width; // client_width;
info.bmiHeader.biHeight = monitor_height; // client_height;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = BitsPerPixel;
info.bmiHeader.biCompression = BI_RGB;
HBITMAP hbitmap = CreateDIBSection(hMemoryDC, &info, DIB_RGB_COLORS, (void**)&my_pixel_data, 0, 0);
SelectObject(hMemoryDC, hbitmap);
BitBlt(hMemoryDC, 0, 0, monitor_width, monitor_height, hScreenDC, 0, 0, SRCCOPY);
int x = 12, y = 12;
int offset = ((monitor_height - 1 - y) * monitor_width + x) * 4;
std::cout << std::format("debug: ({}, {}): ({}, {}, {})\n", x, y, (int)my_pixel_data[offset], (int)my_pixel_data[offset + 1], (int)my_pixel_data[offset + 2], (int)my_pixel_data[offset + 3]);
system("pause");
The output of this will be debug: (12, 12): (199, 76, 133) even though another program has verified the colors are actually (133, 76, 199).
I can easily fix this in my code by flipping the y axis and switching each R and B value and the program will work perfectly well. However, I am just baffled by how this happened and whether there's a more elegant fix.
I can answer the RGB (and it looks like Hans answered the inverted Y axis in a comment). Remember that RGB is stored 0xAARRGGBB, so in that 32 bit value BB is byte 0, GG is byte 1, and RR is byte 2 (alpha is byte 3 if you use it), so when you index in at +0, +1 and +2 you're actually getting the values correctly. When we say RGB we're saying the colors in opposite order of how they're stored in memory.

Algorithm for a red to green colour gradient

I need some mathematical algorithm for converting the value from #FF0000 to #00FF00.
I do not want to go via a black value. The conversion should go from red to some cyan color (light blue) then to turn into green. If just needed to go from FF0000 to 000000 and then to 00FF00 it would be very easy.
The goal is to have levels let say from 0 to 1000 where 0 is #FF0000 and 1000 is #00FF00.
All I need is some smart mapping.
From the comments, I understand you want to be able to show colors between red and green, and that you don't want to "go through black", which I understand as "I want to keep the same intensity".
For this to work, you need to change color spaces. Instead of RGB move to HSL. Take the HSL value for your red, then the HSL value for your green, and interpolate between them in HSL space.
Convert all the intermediate values back to RGB, and there you have your red to green range.
For a general solution, Bart's suggestion to use the HSV space is good. Pooya's and Emilio's answers will interpolate between red and green in the RGB space, which will yield dark yellow/olive in the middle.
If you need a gradient only between red and green, I suggest the following quick solution: Interpolate between red and yellow (#FFFF00) in the first half, and between yellow and green in the second half:
unsigned int red_green_interpol(double x)
{
if (x <= 0.0) return 0xFF0000;
if (x >= 1.0) return 0x00FF00;
if (x < 0.5) {
unsigned int g = 510 * x;
return 0xFF0000 | g << 8;
} else {
unsigned int r = 510 * (1.0 - x);
return 0x00FF00 | r << 16;
}
}
I used a double between 0.0 and 1.0 instead of your integer range from 0 to 1000, but it shouldn't be difficult to adapt it.
maybe something like this :
c :
void print_color ( int value )
{
int r=(255*value)/1000;
printf("#%02x%02x00",r,255-r)
}
edit !
sorry , i didn't read question carefully!
to represent all three colors :
c:
void print_color ( int value )
{
int curr=(3*value)/1000;
int color[3];
color[curr%3]=(255* (value-curr*(1000/3)))/(1000/3);
color[(curr+1)%3]=255-color[curr];
color[(curr+2)%3]=0;
printf("#%02x%02x%02x",color[0],color[1],color[2]);
}
(indexes can be changed for RGB orders...)
well already answered but you wanted something easy:
void gradient()
{
const int _a=3; // color components order
const int _r=2;
const int _g=1;
const int _b=0;
union _col // color
{
DWORD dd; // unsigned 32bit int (your color)
BYTE db[4]; // 4 x BYTE
} col;
col.db[_r]=255; // start color 0x00FF0000
col.db[_g]= 0;
col.db[_b]= 0;
col.db[_a]= 0;
for (int i=0;i<255;i++) // gradient cycle (color 'intensity' = 255)
{
col.db[_r]--;
col.db[_g]++;
// here use your color col.dd;
}
}
for levels case there are only 255 levels in this case so remove for and add this instead (level=i 0..255):
col.db[_r]=255-i;
col.db[_g]= i;
col.db[_b]= 0;
col.db[_a]= 0;

Algorithm to Generate All Possible Black and White Pixel Images in 640 x 360 Dimensions?

I have very minimal programming experience.
I would like to write a program that will generate and save as a gif image every possible image that can be created using only black and white pixels in 640 by 360 px dimensions.
In other words, each pixel can be either black or white. 640 x 360 = 230,400 pixels. So I believe total of 460,800 images are possible to be generated (230,400 x 2 for black/white).
I would like a program to do this automatically.
Please help!
First to answer your questions. Yes there will be writings on "some" pictures. Actually ever text written by human which fits in 640x360 pixels will show up. Also every other text (text not yet written or text that never will be written). Also you will see pictures of every human which is, was or will be alive. See Infinite Monkey Theorem for further information.
The code to create your wanted gif is fairly easy. I used Java for this. Note that you need an extra class: AnimatedGifEncoder. The Code is not memory-bound because the AanimatedGifEncoder will write each image to disk as soon it is computed. But make sure that you have enough disk space available.
import java.awt.Color;
import java.awt.image.BufferedImage;
public class BigPicture {
private final int width;
private final int height;
private final int WHITE = Color.WHITE.getRGB();
private final int BLACK = Color.BLACK.getRGB();
public BigPicture(int width, int height) {
this.width = width;
this.height = height;
}
public void process(String outFile) {
AnimatedGifEncoder gif = new AnimatedGifEncoder();
gif.setSize(width, height);
gif.setTransparent(null); // no transparency
gif.setRepeat(-1); // play only once
gif.setDelay(0); // 0 ms delay between images,
// 'cause ain't nobody got time for that!
gif.start(outFile);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
// set the image to all white
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
bufferedImage.setRGB(x, y, WHITE);
}
}
// add white image
gif.addFrame(bufferedImage);
// add all other combinations
while (increase(bufferedImage)) {
gif.addFrame(bufferedImage);
}
gif.finish();
}
/**
* #param bufferedImage
* the image to increase
* #return false if last pixel set to black => image is complete black
*/
private boolean increase(BufferedImage bufferedImage) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (bufferedImage.getRGB(x, y) == WHITE) {
bufferedImage.setRGB(x, y, BLACK);
return true;
}
bufferedImage.setRGB(x, y, WHITE);
}
}
return false;
}
public static void main(String[] args) {
new BigPicture(640, 360).process("C:\\temp\\bigpicture.gif");
System.out.println("finished.");
}
}
Please be aware that this will take some time. So don't bother waiting and enjoy your life instead! ;)
EDIT: Since my solution is a bit unclear i will explain the algorithm.
I have defined a method called increase. This method takes the BufferedImage and changes the bit pattern of the image so that the next bit pattern appears. The method is just a bit addition. The method will return false if the image encounters the last bit pattern (all pixels are set to black).
As long as it is possible to increase the bit pattern (i.e. increase() returns true) we will save the image as new frame and increase the image again.
How the increase() method works: The method runs over the image first in x-direction then in y-direction. I assume that white pixels are 0 and black pixels are 1. So, we want to take the bit pattern of the image and add 1. We inspect the first pixel: if it is white (0) we can add 1 without an overflow so we turn the pixel to black (0 + 1 = 1 => black pixel). After that we return from the method because we want to increase only one position. It returns true because an increase was possible. If we encounter a black pixel we have an overflow (1 + 1 = 2 or in binary 10). So we have to set the current pixel to white and add the 1 to the next pixel. This will continue until we find the first white pixel.
example:
first we create a print method: this method prints the image as binary number. Attention the number is reversed and the most significant bit is the bit on the right side.
public void print(BufferedImage bufferedImage) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (bufferedImage.getRGB(x, y) == WHITE) {
System.out.print(0); // white pixel
} else {
System.out.print(1); // black pixel
}
}
}
System.out.println();
}
now we modify our main-while loop:
print(bufferedImage); // this one prints the empty image
while (increase(bufferedImage)) {
print(bufferedImage);
}
and now set some short example to test:
new BigPicture(1, 5).process("C:\\temp\\bigpicture.gif");
and finally the output:
00000 // 0 this is the first print before the loop -> "white image"
10000 // 1 the first white pixel is set to black
01000 // 2 the first overflow, so the second pixel is set to black "2"
11000 // 3
00100 // 4
10100 // 5
01100
11100
00010 // 8
10010
01010
11010
00110
10110
01110
11110
00001 // 16
10001
01001
11001
00101
10101
01101
11101
00011
10011
01011
11011
00111
10111
01111
11111 // 31 == 2^5 - 1
finished.
In other words, each pixel can be either black or white. 640 x 360 =
230,400 pixels. So I believe total of 460,800 images are possible to
be generated (230,400 x 2 for black/white).
There is a little flaw in your belief. You are right about the number of pixels: 230,400. Unfortunately, this means there are not 2 * 230,400, but 2 ^ 230,400 possible pictures, which is a number with more than 60,000 digits (longer than the allowed answer size, I am afraid). For comparison a particular number with 45 digits signifies the diameter of the observable universe in centimeters (roughly the width of a pinkie).
In order to understand why your computation of the number of pictures is wrong consider this example: if your pictures contained only three pixels, you could have 8 different pictures (2 ^ 3), rather than 6 (2 * 3). Here are all of them: BBB, BBW, BWB, BWW, WBB, WBW, WWB, WWW. Adding another pixel doubles the size of possible pictures because you can have it white for all the 3-pixel cases, or black for all the 3-pixel cases. Doubling 1 (which is the amount of pictures you can have with 0 pixels) 230,400 times gives you 2 ^ 230,400.
It's great that there is a bounty for the question, but it is rather distracting and counter-productive if it was just as an April's Fool joke.
I'm going to go ahead and pinch some code from a related question, just for fun.
from itertools import product
for matrix in product([0, 1], repeat=(math,pow(2,230400)):
# render and save your .gif
As all the comments have already stated, good luck!
On a more serious note, if you didn't want to be absolutely sure that you had all permutations, you could generate a random 640x360 matrix and store it as an image.
Perform this action say 100k times, and you'll have at least an interesting set of pictures to look at, but it's unfeasible to get every possible permutation.
You could then delete all identical files to reduce the set to just the unique images.

Keeping only the red/green/blue part of the image

I have made a very basic algorithm which extracts only the red / green / blue pixels of the image and displays them. However, it works well on some images and produces unexpected results in some. Like when I want to keep only green , it also keeps turquoise.
Turquoise is a shade of green but it is not what I want to display. I only want things that are 'visually' green.
Here is a sample output that shows what has gone wrong:
The algorithm picked up the turquoiose color of the flower pot on which the dog sits. The original image is here.
My algorithm is below (for the green one.) All the algorithms are akin to each other.
void keepGreen() {
for (int i = 0; // iterate over the pixels of the image
i < img.pixels.length;
i++) {
float inputRed = red(img.pixels[i]); // extract red
float inputGreen = green(img.pixels[i]); // extract green
float inputBlue = blue(img.pixels[i]); // extract blue
int pixel = -1;
float outputRed = -1;
float outputGreen = -1;
float outputBlue = -1;
if(inputRed <= inputGreen*0.9 && inputBlue <= inputGreen*0.9){ // check if the pixel is visually green
outputRed = inputRed; // yes, let it stay
outputGreen = inputGreen;
outputBlue = inputBlue;
}else{ // no, make it gray
int mostProminent =(int) max(inputRed, inputGreen, inputBlue);
int leastProminent =(int) min(inputRed, inputGreen, inputBlue);
int avg = (int) ((mostProminent + leastProminent) / 2);
outputRed = avg;
outputGreen = avg;
outputBlue = avg;
pixel = color(avg, avg, avg);
}
img.pixels[i] = color(outputRed, outputGreen, outputBlue); // set the pixel to the new value
}
img.updatePixels(); // update the image
image(img, WIDTH/2, HEIGHT/2, calculatedWidth, calculatedHeight); // display
}
How can I avoid those errors ?
Experiment with raising the red and blue thresholds individually, i.e inputGreen * 0.8 instead of inputGreen * 0.9 Use a tool like Instant Eyedropper or Pixel Picker to verify the RGB values in those colors that you don't want, and use that as feedback to set the thresholds for elimination of the colors that you don't want.
You might also want to consider the luminance level in your calculations. The pixels being picked up on the flower pot are darker than the other pixels on the flower pot.
Just because Blue is less than Green doesn't mean the pixel doesn't look green. For example, turquoise might be red=50, blue=200, green=150. Perhaps you need to (also) gray out pixels that have substantial green in their own right, regardless of red/blue.

Separating Background and Foreground

I am new to Matlab and to Image Processing as well. I am working on separating background and foreground in images like this
I have hundreds of images like this, found here. By trial and error I found out a threshold (in RGB space): the red layer is always less than 150 and the green and blue layers are greater than 150 where the background is.
so if my RGB image is I and my r,g and b layers are
redMatrix = I(:,:,1);
greenMatrix = I(:,:,2);
blueMatrix = I(:,:,3);
by finding coordinates where in red, green and blue the values are greater or less than 150 I can get the coordinates of the background like
[r1 c1] = find(redMatrix < 150);
[r2 c2] = find(greenMatrix > 150);
[r3 c3] = find(blueMatrix > 150);
now I get coordinates of thousands of pixels in r1,c1,r2,c2,r3 and c3.
My questions:
How to find common values, like the coordinates of the pixels where red is less than 150 and green and blue are greater than 150?
I have to iterate every coordinate of r1 and c1 and check if they occur in r2 c2 and r3 c3 to check it is a common point. but that would be very expensive.
Can this be achieved without a loop ?
If somehow I came up with common points like [commonR commonC] and commonR and commonC are both of order 5000 X 1, so to access this background pixel of Image I, I have to access first commonR then commonC and then access image I like
I(commonR(i,1),commonC(i,1))
that is expensive too. So again my question is can this be done without loop.
Any help would be appreciated.
I got solution with #Science_Fiction answer's
Just elaborating his/her answer
I used
mask = I(:,:,1) < 150 & I(:,:,2) > 150 & I(:,:,3) > 150;
No loop is needed. You could do it like this:
I = imread('image.jpg');
redMatrix = I(:,:,1);
greenMatrix = I(:,:,2);
blueMatrix = I(:,:,3);
J(:,:,1) = redMatrix < 150;
J(:,:,2) = greenMatrix > 150;
J(:,:,3) = blueMatrix > 150;
J = 255 * uint8(J);
imshow(J);
A greyscale image would also suffice to separate the background.
K = ((redMatrix < 150) + (greenMatrix > 150) + (blueMatrix > 150))/3;
imshow(K);
EDIT
I had another look, also using the other images you linked to.
Given the variance in background colors, I thought you would get better results deriving a threshold value from the image histogram instead of hardcoding it.
Occasionally, this algorithm is a little to rigorous, e.g. erasing part of the clothes together with the background. But I think over 90% of the images are separated pretty well, which is more robust than what you could hope to achieve with a fixed threshold.
close all;
path = 'C:\path\to\CUHK_training_cropped_photos\photos';
files = dir(path);
bins = 16;
for f = 3:numel(files)
fprintf('%i/%i\n', f, numel(files));
file = files(f);
if isempty(strfind(file.name, 'jpg'))
continue
end
I = imread([path filesep file.name]);
% Take the histogram of the blue channel
B = I(:,:,3);
h = imhist(B, bins);
h2 = h(bins/2:end);
% Find the most common bin in the *upper half*
% of the histogram
m = bins/2 + find(h2 == max(h2));
% Set the threshold value somewhat below
% the value corresponding to that bin
thr = m/bins - .25;
BW = im2bw(B, thr);
% Pad with ones to ensure background connectivity
BW = padarray(BW, [1 1], 1);
% Find connected regions in BW image
CC = bwconncomp(BW);
L = labelmatrix(CC);
% Crop back again
L = L(2:end-1,2:end-1);
% Set the largest region in the orignal image to white
for c = 1:3
channel = I(:,:,c);
channel(L==1) = 255;
I(:,:,c) = channel;
end
% Show the results with a pause every 16 images
subplot(4,4,mod(f-3,16)+1);
imshow(I);
title(sprintf('Img %i, thr %.3f', f, thr));
if mod(f-3,16)+1 == 16
pause
clf
end
end
pause
close all;
Results:
Your approach seems basic but decent. Since for this particular image the background is composed of mainly blue so you be crude and do:
mask = img(:,:,3) > 150;
This will set those pixels which evaluate to true for > 150 to 0 and false to 1. You will have a black and white image though.
imshow(mask);
To add colour back
mask3d(:,:,1) = mask;
mask3d(:,:,2) = mask;
mask3d(:,:,3) = mask;
img(mask3d) = 255;
imshow(img);
Should give you the colour image of face hopefully, with a pure white background. All this requires some trial and error.

Resources