1 of 44

Ch 10 – Image Processing

Supanit Angsirikul

2 of 44

Processing image with

  • Resize
  • Create and copy
  • Get
  • Set
  • Load and Update Pixels
  • Blur
  • Color Modification
  • Turning background to transparency
  • Custom Tone
  • Pixelation Effect (raster image look)
  • To create Paint Brush effects :
    • Pointillism
    • Copying Neighbors
    • Displacement
  • Edge Detection and Noise Filter
  • Flickering (Off-Screen Drawing)
  • Steganography
  • Masking Lower Bits - To conceal a picture (secret) in a picture (cover)

3 of 44

Draw on Image

size(200, 200);

PImage im = loadImage(“data/android1.png");

im.resize(width, height);

image(im, 0, 0);

// draw a circle

fill(255, 0, 0, 150);

noStroke();

ellipse(width/2, height/2 + 10, 30, 30);

save("test.png");

4 of 44

Scale Image (icon or thumbnail)

PImage android = loadImage(“data/android1.png");

android.resize(50, 50); // resize() Enlarge or reduce the image size

android.save("test.png");

5 of 44

Add Image on Image

size(200, 200);

PImage android = loadImage(“data/android1.png");

PImage blue = loadImage(“data/blue.png");

android.resize(width, height);

image(android, 0, 0); //Draw an image on the sketch canvas

image(blue, 150, 10);

6 of 44

Create and Copy

  • createImage(w, h, format)
  • Create a PImage with size (w x h) and format, RGB, ARGB, ALPHA.

  • To copy a source image to a destination image,
  • the destination image must be pre-created with the same size.
  • copy(sx, sy, sw, sh, dx, dy, dw, dh)
  • copy(src, sx, sy, sw, sh, dx, dy, dw, dh)
  • copy() returns void.
  • If no src the image will be copied from its self to its self.

7 of 44

Create and Copy

size(600, 200);

// 1. Original

PImage im1 = loadImage("data/android2.jpg");

image(im1, 0, 0);

// 2. Copy from original

PImage im2 = createImage(im1.width, im1.height, RGB);

im2.copy(im1, 0, 0, im1.width/2, im1.height/2,

0, 0, im1.width, im1.height);

image(im2, 200, 0);

// 3. Copy to itself

im1.copy(50, 0, 50, 50, 0, 0, 100, 100);

image(im1, 400, 0);

8 of 44

Get Pixel and Image On Screen

size(200, 200);

background(255);

noStroke();

fill(0, 255, 0);

ellipse(width/2, height/2, 100, 100);

/* Get pixel on Screen

get(x, y) get a pixel at x,y

*/

color c = get(width/2, height/2);

println(red(c) + ", " + green(c) + ", " + blue(c));

/* Get image on Screen

get(x, y, w, h) get image at x,y with size w. h.

get() get image of the screen

*/

PImage im = get(width/2, height/2, 50, 50);

image(im, 0, 0);

9 of 44

Set Pixel On Screen

/* Set pixel On Screen

set(x, y, color) set a pixel(color) at x, y

*/

size(255, 255);

for (int i = 0; i < width; i++)

for (int j = 0; j < height; j++)

set(i, j, color(0, i, j));

10 of 44

Set Image On Screen

/* Set image On Screen

set(x, y, img) set image at x, y

*/

size(400, 400);

PImage im = loadImage(“data/android1.png");

set(200, 200, im);

// image(im, 200, 200);

11 of 44

Get and Set on Image

/* Get and Set on Image

image.get(x, y, w, h)

image.get()

image.set(x, y, img)

*/

size(250, 250);

PImage android = loadImage("data/android2.jpg");

android.resize(width, height);

PImage red = loadImage("data/red.png");

PImage redx = red.get(0, 0, 38, 58);

android.set(10, 0, redx);

image(android, 0, 0);

12 of 44

Load and Update Pixels

  • Accessing pixels on screen is time consuming.

  • loadPixels() - Create an array of colors pixels[]

  • updatePixels() -Update pixels from pixels[] to the screen.

  • Since pixels[] is a one dimension array that represents a 2D image, so get and set we need a mapping from 2D to 1D.

  • For a two dimensions array a[width][height]:
    • a[x][y] == a[0][0] + x * width + y
    • a[0][0] is the address of the first element.

  • so the pixel at x, y is at x*width + y

13 of 44

Load and Update Pixels

size(255, 255);

loadPixels();

for (int g = 0; g < height; g++)

for (int r = 0; r < width; r++)

pixels[g*width + r] = color(r, g, 0);

updatePixels();

14 of 44

Load and Update Pixels

size(255, 255);

loadPixels();

for (int y = 30; y < height-30; y++)

for (int x = 60; x < width-60; x++)

pixels[y*width + x] = color(0,0,0);

updatePixels();

15 of 44

Load and Update Pixels

size(200, 200);

int cx = width/2;

int cy = height/2;

int rsq1 = 50*50;

int rsq2 = 51 * 51;

loadPixels();

for(int x = -cx; x < cx; x++)

for(int y = -cy; y < cy; y++)

if (x*x + y*y > rsq1 && x*x + y*y < rsq2)

pixels[(y + cy)*width + (x+cx)] = color(0,0,0);

updatePixels();

16 of 44

To Manipulate pixels in image:

1. We can load image and show on the screen then create pixels[] from the screen.

2. We can create pixels[] from the image directly by

<p-image>.loadPixels()

and then

<p-image>.updatePixels()

to copy back to the image.

17 of 44

Manipulate pixels in image

size(200, 200);

PImage im = loadImage(“data/android2.jpg");

im.resize(width, height);

im.loadPixels();

for (int x = 50; x < 150; x++)

im.pixels[x*width + x] = color(255,0,0);

im.updatePixels();

image(im, 0, 0);

18 of 44

Few Lines Blur - To blur a picture. We can insert a white line in every few lines.

void setup() {

size(400, 400);

noLoop();

}

int step = 3;

void draw() {

PImage im = loadImage(“data/android2.jpg");

im.resize(width, height);

im.loadPixels();

for (int i = 0; i < im.pixels.length; i+=step)

im.pixels[i] = color(255, 255, 255);

im.updatePixels();

image(im, 0, 0);

}

void keyPressed() {

if (key == 'a')

++step;

else if (key == 'z')

--step;

println(step);

redraw();

}

19 of 44

Color Modification - Pixel colors can be modified unrelated to their locations.

size(400, 400);

background(190, 250, 0);

PImage im = loadImage("data/android2.jpg");

im.resize(width, height);

im.loadPixels();

for (int i = 0; i < im.pixels.length; i++) {

color c = im.pixels[i];

float r = red(c); //Retrieve the red value

float g = green(c); //Retrieve the green value

float b = blue(c); //Retrieve the blue value

im.pixels[i] = color(r, 0, b); // remove green

// im.pixels[i] = color(b, g, r); // red <-> blue

// im.pixels[i] = color(g, r, b); // red <-> green

// im.pixels[i] = color(r, b, g); // green <-> blue

// im.pixels[i] = color(r, g, b, 170); // alpha 170

// im.pixels[i] = color(r, g, b, (255 - i % im.width * 0.5));

}

im.updatePixels();

image(im, 0, 0);

20 of 44

Turning background to transparency

int threshold = 730;

size(800, 400);

PImage im1 = loadImage(“data/android2.jpg");

im1.resize(width/2, height);

background(190, 250, 0); // light green background

image(im1, 0, 0);

// create an image that supports alpha.

PImage im2 = createImage(im1.width, im1.height, ARGB);

im1.loadPixels();

im2.loadPixels();

for (int i = 0; i < im1.pixels.length; i++) {

float r = red(im1.pixels[i]);

float g = green(im1.pixels[i]);

float b = blue(im1.pixels[i]);

if ((r + g + b) > threshold)

im2.pixels[i] = color(r, g, b, 0);

else

im2.pixels[i] = color(r, g, b, 255);

}

im2.updatePixels();

// im2.save("/data/tranAndroid.png");

image(im2, im1.width, 0);

21 of 44

Negative, Grayscale, Sepia

void setup() {

size(1000, 800);

PImage im = loadImage(“data/p1.jpg");

im.resize(width/2, height/2);

image(im, 0, 0);

negative();

gray();

sepia();

}

void negative() {

PImage im = loadImage(“data/p1.jpg");

im.resize(width/2, height/2);

im.loadPixels();

for (int i = 0; i < im.pixels.length; i++) {

color c = im.pixels[i];

im.pixels[i] = color(255-red(c), 255-green(c), 255-blue(c));

}

im.updatePixels();

image(im, width/2, 0);

}

void gray() {

PImage im = loadImage(“data/p1.jpg");

im.resize(width/2, height/2);

im.loadPixels();

for (int i = 0; i < im.pixels.length; i++) {

color c = im.pixels[i];

im.pixels[i] = color(red(c)*0.3 + green(c)*0.59 + blue(c)*0.11);

}

im.updatePixels();

image(im, 0, height/2);

}

void sepia() {

PImage im = loadImage(“data/p1.jpg");

im.resize(width/2, height/2);

im.loadPixels();

for (int i = 0; i < im.pixels.length; i++)

im.pixels[i] = sepia(im.pixels[i]);

im.updatePixels();

image(im, width/2, height/2);

}

// Sepia tones is a warmer coloring that is used in archiving grayscale photos.

color sepia(color c) {

float r = red(c)*0.393 + green(c)*0.769 + blue(c)*0.189;

float g = red(c)*0.349 + green(c)*0.686 + blue(c)*0.168;

float b = red(c)*0.272 + green(c)*0.534 + blue(c)*0.131;

return color(r, g, b);

}

22 of 44

Negative, Grayscale, Sepia

23 of 44

Custom Tone

int r = 255;

int g = 240; // change green value to 220 for a red-toned sepia palette

int b = 192;

color[] palette = new color[256];

void setupPalette() {

for (int i = 0; i < palette.length; i++) {

palette[i] = color(r*i/255, g*i/255, b*i/255);

}

}

color custom(color c) {

float gray = red(c)*0.3 + green(c)*0.59 + blue(c)*0.11;

return palette[int(gray)];

}

void setup() {

size(1000, 800);

setupPalette();

noLoop();

}

void draw() {

PImage im = loadImage(“data/p1.jpg");

im.resize(width, height);

im.loadPixels();

for (int i = 0; i < im.pixels.length; i++)

im.pixels[i] = custom(im.pixels[i]);

im.updatePixels();

image(im, 0, 0);

}

void keyPressed() {

if (key == 'a')

r += 10;

else if (key == 's')

g += 10;

else if (key == 'd')

b += 10;

else if (key == 'z')

r -= 10;

else if (key == 'x')

g -= 10;

else if (key == 'c')

b -= 10;

println(r + "," + g + "," + b);

setupPalette();

redraw();

}

24 of 44

Custom Tone

25 of 44

Pixelation Effect (to create a raster image look)

int row = 100, column = 100;

void setup() {

size(1000, 800);

noLoop();

}

void draw() {

PImage im = loadImage(“data/p6.jpg");

im.resize(width, height);

int dx = width/column;

int dy = height/row;

for (int y = 0; y < height; y += dy)

for (int x = 0; x < width; x += dx) {

fill(im.get(x, y));

rect(x, y, dx, dy);

}

}

void keyPressed() {

if (key == 'a')

row += 2;

else if (key == 'z')

row -= 2;

else if (key == 's')

column += 2;

else if (key == 'x')

column -= 2;

println(row + "," + column);

redraw();

}

26 of 44

Pointillism: To create paint effect.

float pointSize = 7;

int pointNumber = 15000;

void setup() {

size(1000, 800);

noStroke();

noLoop();

}

void draw() {

PImage im = loadImage(“data/road.png");

im.resize(width, height);

image(im, 0, 0);

for (int i = 0; i < pointNumber; i++) {

int x = (int)random(width);

int y = (int)random(height);

color c = get(x, y);

fill(c);

ellipse(x, y, pointSize, pointSize);

}

}

void keyPressed() {

if (key == 'a')

pointSize++;

else if (key == 'z')

pointSize--;

else if (key == 's')

pointNumber += 10;

else if (key == 'x')

pointNumber -= 10;

println(pointSize + "," + pointNumber);

redraw();

}

27 of 44

Copying Neighbors: to create Paint Brush effects.

int round = 3;

void setup() {

size(1000, 800);

noLoop();

}

void draw() {

PImage im = loadImage(“data/pic1.jpg");

im.resize(width, height);

image(im, 0, 0);

for (int r = 0; r < round; r++) {

for (int x = 1; x < width-1; x++)

for (int y = 1; y < height-1; y++)

set(x, y, get(int(random(x-1, x+2)), int(random(y-1, y+2))));

}

}

void keyPressed() {

if (key == 'a')

round++;

else if (key == 'z')

round--;

println(round);

redraw();

}

28 of 44

Displacement: to create Paint Brush effects.

int size = 1;

void setup() {

size(1000, 800);

noLoop();

noStroke();

}

void draw() {

PImage im = loadImage(“data/pic1.jpg");

im.resize(width, height);

image(im, 0, 0);

for (int x = 2; x < width; x++)

for (int y = 2; y < height; y++) {

int xx = x + int(random(-size, size));

int yy = y + int(random(-size, size));

fill(get(x, y));

rect(xx-size-1, yy-size-1, size, size);

}

}

void keyPressed() {

if (key == 'a')

size++;

else if (key == 'z')

size--;

println(size);

redraw();

}

29 of 44

Edge Detection and Noise Filter

int threshold = 5;

boolean rmNoise = false;

void setup() {

size(400, 400);

noLoop();

}

void draw() {

PImage im = loadImage(“data/android2.jpg");

im.resize(width, height);

image(im, 0, 0);

edgeDetect();

if (rmNoise)

removeNoise();

}

void edgeDetect() {

for (int y = 0; y < height - 1; y++)

for (int x = 0; x < width - 1; x++) {

float a = red(get(x, y));

float b = red(get(x+1, y));

if (abs(a - b) > threshold)

set(x, y, color(0));

else

set(x, y, color(255));

}

}

void removeNoise() {

for (int y = 0; y < height - 1; y++)

for (int x = 0; x < width - 1; x++) {

float a = red(get(x, y));

float b = red(get(x+1, y));

float c = red(get(x-1, y));

float d = red(get(x, y-1));

float e = red(get(x, y+1));

if ((abs(a - b) > threshold) && (abs(a - c) > threshold) ||

(abs(a - d) > threshold) && (abs(a - e) > threshold))

set(x, y, color(255));

}

}

void keyPressed() {

if (key == 'a')

threshold++;

else if (key == 'z')

threshold--;

else if (key == 's')

rmNoise = !rmNoise;

println(threshold + "," + rmNoise);

redraw();

}

30 of 44

Edge Detection and Noise Filter

31 of 44

Neighbors Edge Detection

int xd[] = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };

int yd[] = { 1, 1, 0, -1, -1, -1, 0, 1, 1 };

size(1000, 800);

PImage im = loadImage(“data/pic1.jpg");

im.resize(width, height);

image(im, 0, 0);

int m[][] = new int[width][height];

for (int x = 1; x < width-1; x++)

for (int y = 1; y < height-1; y++) {

int a = 0, b = 0;

for(int i = 0; i < 8; i++) {

float b1 = brightness(get(x+xd[i], y+yd[i]));

float b2 = brightness(get(x+xd[i+1], y+yd[i+1]));

if (b1 < 128)

b++;

if (b1 < 128 && b2 > 128)

a++;

}

if ((b >= 2 && b <= 6) || a == 1)

m[x][y] = 1;

else

m[x][y] = 0;

}

for (int x = 1; x < width-1; x++)

for (int y = 1; y < height-1; y++) {

if (m[x][y] == 1)

set(x, y, color(0));

else

set(x, y, color(255));

}

32 of 44

Flickering

  • draw() paints directly to the srceen.
  • If there are too many computations per screen that will cause flickering.

  • Processing support off screen drawing internally.

33 of 44

Flickering

PImage im;

float x = 0, cy = 130, r = 250;

void setup() {

size(1000, 800);

im = loadImage(“data/road.png");

im.resize(width, height);

noStroke();

}

void draw() {

image(im, 0, 0);

fill(255, 0, 0);

ellipse(++x, cy, r, r);

if (x > width)

noLoop();

}

34 of 44

Off-Screen Drawing

  • PGraphics

createGraphics(w, h)

  • We can draw on PGraphics just like on the screen and
  • PGraphics can be shown on the screen using image().

  • All drawing functions must be between beginDraw()
  • and endDraw().

35 of 44

Off-Screen Drawing

PImage im;

PGraphics pg;

float x = 0, cy = 130, r = 250;

void setup() {

size(1000, 800);

im = loadImage(“data/road.png");

im.resize(width, height);

pg = createGraphics(width, height);

}

void draw() {

pg.beginDraw();

pg.image(im, 0, 0);

pg.fill(255, 0, 0);

pg.noStroke();

pg.ellipse(++x, cy, r, r);

pg.endDraw();

image(pg, 0, 0);

if (x > width)

noLoop();

}

36 of 44

without PGraphics

void setup() {

size(900, 700);

smooth();

stroke(255);

}

void draw() {

background(0);

for (int i = 0; i < 2000; i++)

line(mouseX, mouseY, random(width), random(height));

}

37 of 44

with PGraphics

PGraphics pg;

void setup() {

size(900, 700);

pg = createGraphics(width, height);

}

void draw() {

pg.beginDraw();

pg.background(0);

pg.stroke(255);

pg.smooth();

for (int i = 0; i < 2000; i++)

pg.line(mouseX, mouseY, random(width), random(height));

pg.endDraw();

image(pg, 0, 0);

}

38 of 44

Steganography : Encoding Signature in Picture

int signature = 123;

int signaturePos = 12345;

color cover;

PImage im;

void setup() {

size(600, 400);

im = loadImage("data/pic1s.jpg");

im.resize(width, height);

image(im, 0, 0);

}

void draw() {

}

void keyPressed() {

if (key == 'e') {

im.loadPixels();

cover = im.pixels[signaturePos];

im.pixels[signaturePos] ^= signature; //^ bitwise exclusieve OR

im.updatePixels();

image(im, 0, 0);

save("data/encoded.png");

println("Encode Ok.");

}

if (key == 'd') {

im = loadImage("data/encoded.png");

im.loadPixels();

color c = im.pixels[signaturePos];

println(c ^ cover);

}

}

39 of 44

Steganography : Encoding Signature in Picture

40 of 44

Masking Lower Bits - To conceal a picture (secret) in a picture (cover)

PImage cover; // To hold unencoded cover picture

PImage secret; // To hold image to be encoded into the cover, must be the same size

int clearSecretMask = 0xFF0F0F0F; // & with color to remove rightmost 4 bits from all R/G/B channels

int clearCoverMask = 0x000F0F0F;

void setup() {

size(1200, 800);

cover = loadImage(“data/pic2s.jpg");

secret = loadImage(“data/pic6s.jpg");

cover.resize(600, 400); // Size the sketch to be the same size as the cover

secret.resize(600, 400);

image(cover, 0, 0);

image(secret, 600, 0);

}

41 of 44

Masking Lower Bits - To conceal a picture (secret) in a picture (cover) (ต่อ)

void draw() { }

void keyPressed() {

if (key == 'e') {

cover.loadPixels(); // Load pixels for the cover picture

secret.loadPixels(); // Load pixels for the image to be encoded

for (int i = 0; i < cover.pixels.length; i++) {

color cp = cover.pixels[i];

color sp = secret.pixels[i]; //strip off the alpha and the least significant 4 bits of R, G and B

sp = sp & ~ clearSecretMask; // right shift so that the most significant 4 bits

sp = sp >> 4; // Strip off least significant 4 bits of R, G, B from cover image

cp = cp & ~clearCoverMask; // Add the significant bits from the secret image to the cover's least significant bits

cp = cp | sp; // Replace the encoded pixel back

cover.pixels[i] = cp;

}

cover.updatePixels();

image(cover, 0, 400);

}

42 of 44

Masking Lower Bits - To conceal a picture (secret) in a picture (cover) (cont.)

if (key == 'd') {

int clearMask = 0x00F0F0F0;

cover.loadPixels();

for (int i = 0; i < cover.pixels.length; i++) {

color c = cover.pixels[i];

c = c & ~clearMask; // left shift and move the least significant 4 bits into the leftmost position

c = c << 4; // Replace the decoded pixel back into the picture

cover.pixels[i] = c;

}

cover.updatePixels();

image(cover, 600, 400);

}

}

43 of 44

44 of 44