forked from jdf/processing-py-site
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.html
More file actions
726 lines (653 loc) · 31.9 KB
/
index.html
File metadata and controls
726 lines (653 loc) · 31.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta name="generator" content=
"HTML Tidy for Mac OS X (vers 31 October 2006 - Apple Inc. build 15.12), see www.w3.org">
<title></title>
</head>
<body>
<h1>Images and Pixels</h1>
<table width="656">
<tr>
<td>
<p class="license">This tutorial is for Processing's Python Mode.
If you see any errors or have comments, please <a href=
"https://github.com/jdf/processing-py-site/issues?state=open">
let us know</a>. This tutorial is adapted from the book, <a href=
"http://www.processing.org/learning/books/#shiffman">Learning
Processing</a>, by Daniel Shiffman, published by Morgan Kaufmann
Publishers, Copyright © 2008 Elsevier Inc. All rights
reserved. </p>
<p> </p>
<p>
A digital image is nothing more than data -- numbers indicating variations of red, green, and blue at a particular location on a grid of pixels. Most of the time, we view these pixels as miniature rectangles sandwiched together on a computer screen. With a little creative thinking and some lower level manipulation of pixels with code, however, we can display that information in a myriad of ways. This tutorial is dedicated to breaking out of simple shape drawing in Processing and using images (and their pixels) as the building blocks of Processing graphics. </p>
<h3>Getting started with images.</h3>
<p>
Hopefully, you are comfortable with the idea of data types. You probably specify them often -- a float variable "speed", an int "x", etc. These are all primitive data types, bits sitting in the computer's memory ready for our use. Though perhaps a bit trickier, you hopefully also use objects, complex data types that store multiple pieces of data (along with functionality) -- a "Ball" class, for example, might include floating point variables for location, size, and speed as well as methods to move, display itself, and so on.
<br /><br />
In addition to user-defined objects (such as Ball), Processing has a bunch of handy classes all ready to go without us writing any code. In this tutorial, we'll examine <a href="http://py.processing.org/reference/PImage.html">PImage</a>, a class for loading and displaying an image as well as looking at its pixels.
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-1/">Example: "Hello World" images</a>
<br /> </p>
<pre>
def setup():
global img
size(320,240)
# Make a new instance of a PImage by loading an image file
# Declaring a variable of type PImage
img = loadImage("mysummervacation.jpg")
def draw():
global img
background(0);
# Draw the image to the screen at coordinate (0,0)
image(img,0,0)
</pre>
<br />
<p>
Using an instance of a <a href="http://py.processing.org/reference/PImage.html">PImage</a> object is no different than using a user-defined class.
We declare a variable img and assign a newly created instance of the <a href="http://py.processing.org/reference/PImage.html">PImage</a> class to it by calling the <a href="http://py.processing.org/reference/loadImage.html"></a>. <a href="http://py.processing.org/reference/loadImage.html">loadImage()</a> takes one argument, a String indicating a file name, and loads the that file into memory. <a href="http://py.processing.org/reference/loadImage.html">loadImage()</a> looks for image files stored in your Processing sketch's "data" folder.
<br /><br />
<table width="100%" border="0" cellpadding="20" bgcolor="#DEDEDE">
<tr><td>
<strong>The Data Folder: How do I get there?</strong>
<br /><br />
Images can be added to the data folder automatically via:
<br /><br />
Sketch --> Add File. . .
<br /><br />
or manually:
<br /><br />
Sketch --> Show Sketch Folder
<br /><br />
This will open up the sketch folder. If there is no data directory create one. Otherwise, place your image files inside.
<br /><br />
Processing accepts the following file formats for images: GIF, JPG, TGA, PNG.
</td></tr></table>
<br />
<img src="imgs/datafolder.jpg"><br/>
<br /><br />
In the above example, it may seem a bit peculiar that we never called a "constructor" to instantiate the <a href="http://py.processing.org/reference/PImage.html">PImage</a> object, saying "PImage()". After all, in most object-related examples, a constructor is a must for producing an object instance.
<br /><br />
</p>
<pre>
mySpaceship = Spaceship();
myFlower = Flower(25);
</pre>
<p>
<br /><br />
yet:
<br />
</p>
<pre>
img = loadImage("file.jpg");
</pre>
<br />
<p>
In fact, the <a href="http://py.processing.org/reference/loadImage.html">loadImage()</a> function performs the work of a constructor, returning a brand new instance of a <a href="http://py.processing.org/reference/PImage.html">PImage</a> object generated from the specified filename. We can think of it as the <a href="http://py.processing.org/reference/PImage.html">PImage</a> constructor for loading images from a file. For creating a blank image, the <a href="http://py.processing.org/reference/createImage.html">createImage()</a> function is used.
<br /><br />
</p>
<pre>
# Create a blank image, 200x200 pixels with RGB color
img = createImage(200,200,RGB)
</pre>
<p>
<br />
We should also note that the process of loading the image from the hard drive into memory is a slow one, and we should make sure our program only has to do it once, in setup(). Loading images in draw() may result in slow performance as well as "Out of Memory" errors.
<br /><br />
Once the image is loaded, it is displayed with the <a href="http://py.processing.org/reference/image.html">image()</a> function. The <a href="http://py.processing.org/reference/image.html">image()</a> function must include 3 arguments -- the image to be displayed, the x location, and the y location. Optionally two arguments can be added to resize the image to a certain width and height.
<br /><br />
</p>
<pre>
image(img,10,20,90,60)
</pre>
<h3>Your very first image processing filter</h3>
<p>
When displaying an image, you might like to alter its appearance. Perhaps you would like the image to appear darker, transparent, blue-ish, etc. This type of simple image filtering is achieved with Processing's <a href="http://py.processing.org/reference/tint.html">tint()</a> function. <a href="http://py.processing.org/reference/tint.html">tint()</a> is essentially the image equivalent of shape's fill(), setting the color and alpha transparency for displaying an image on screen. An image, nevertheless, is not usually all one color. The arguments for <a href="http://py.processing.org/reference/tint.html">tint()</a> simply specify how much of a given color to use for every pixel of that image, as well as how transparent those pixels should appear.
<br /><br />
For the following examples, we will assume that two images (a sunflower and a dog) have been loaded and the dog is displayed as the background (which will allow us demonstrate transparency.)
<br /><br />
</p>
<pre>
sunflower = loadImage("sunflower.jpg")
dog = loadImage("dog.jpg")
background(dog)
</pre>
<p>
<br />
If <a href="http://py.processing.org/reference/tint.html">tint()</a> receives one argument, only the brightness of the image is affected.
<br /><br />
</p>
<img src="imgs/tint1.jpg">
<pre>
# The image retains its original state.
tint(255)
image(sunflower,0,0)
</pre>
<br /><br />
<img src="imgs/tint2.jpg"><br />
<pre>
# The image appears darker.
tint(100)
image(sunflower,0,0)
</pre>
<p>
<br />
A second argument will change the image's alpha transparency.
<br /><br />
</p>
<img src="imgs/tint3.jpg"><br />
<pre>
# The image is at 50% opacity.
tint(255,127)
image(sunflower,0,0)
</pre>
<p>
<br />
Three arguments affect the brightness of the red, green, and blue components of each color.
<br /><br />
</p>
<img src="imgs/tint4.jpg">
<pre>
# None of its red, most of its green, and all of its blue.
tint(0,200,255)
image(sunflower,0,0)
</pre>
<p>
<br />
Finally, adding a fourth argument to the method manipulates the alpha (same as with 2). Incidentally, the range of values for tint() can be specified with colorMode().
<br /><br />
</p>
<img src="imgs/tint5.jpg">
<pre>
# The image is tinted red and transparent.
tint(255,0,0,100)
image(sunflower,0,0)
</pre>
<h3>Pixels, pixels, and more pixels</h3>
<p>
If you've just begun using Processing you may have mistakenly thought that the only offered means for drawing to the screen is through a function call. "Draw a line between these points" or "Fill an ellipse with red" or "load this JPG image and place it on the screen here." But somewhere, somehow, someone had to write code that translates these function calls into setting the individual pixels on the screen to reflect the requested shape. A line doesn't appear because we say <a href="http://py.processing.org/reference/line.html">line()</a>, it appears because we color all the pixels along a linear path between two points. Fortunately, we don't have to manage this lower-level-pixel-setting on a day-to-day basis. We have the developers of Processing (and Java) to thank for the many drawing functions that take care of this business.
<br /><br />
Nevertheless, from time to time, we do want to break out of our mundane shape drawing existence and deal with the pixels on the screen directly. Processing provides this functionality via the pixels array.
<br /><br />
We are familiar with the idea of each pixel on the screen having an X and Y position in a two dimensional window. However, the array pixels has only one dimension, storing color values in linear sequence.
<br /><br />
</p>
<img src="imgs/pixelarray.jpg">
<p>
<br /><br />
Take the following simple example. This program sets each pixel in a window to a random grayscale value. The pixels array is just like any other array, the only difference is that we don't have to declare it since it is a Processing built-in variable.
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-5-setting-pixels/">Example: Setting Pixels</a>
<br /><br />
</p>
<pre>
def setup():
size(200, 200)
# Before we deal with pixels
loadPixels()
changePixels()
def changePixels():
# Loop through every pixel
for i in xrange(len(pixels)):
# Pick a random number, 0 to 255
rand = random(255)
# Create a grayscale color based on random number
c = color(rand)
# Set pixel at that location to random color
pixels[i] = c
# When we are finished dealing with pixels
updatePixels()
</pre>
<p>
<br />
First, we should point out something important in the above example. Whenever you are accessing the pixels of a Processing window, you must alert Processing to this activity. This is accomplished with two functions:
</p>
<ul>
<li><a href="http://py.processing.org/reference/loadPixels.html">loadPixels()</a> This function is called before you access the pixel array, saying "load the pixels, I would like to speak with them!"</li>
<li><a href="http://py.processing.org/reference/updatePixels.html">updatePixels()</a> This function is called after you finish with the pixel array saying "Go ahead and update the pixels, I'm all done!"</li>
</ul>
<p>
In the above example, because the colors are set randomly, we didn't have to worry about where the pixels are onscreen as we access them, since we are simply setting all the pixels with no regard to their relative location. However, in many image processing applications, the XY location of the pixels themselves is crucial information. A simple example of this might be, set every even column of pixels to white and every odd to black. How could you do this with a one dimensional pixel array? How do you know what column or row any given pixel is in?
<br /><br />
In programming with pixels, we need to be able to think of every pixel as living in a two dimensional world, but continue to access the data in one (since that is how it is made available to us). We can do this via the following formula:
<br /><br />
1. Assume a window or image with a given WIDTH and HEIGHT.<br />
2. We then know the pixel array has a total number of elements equaling WIDTH * HEIGHT.<br />
3. For any given X, Y point in the window, the location in our 1 dimensional pixel array is:
<br /><br />
</p>
<strong>LOCATION = X + Y*WIDTH </strong>
<br /><br />
<img src="imgs/pixelarray2d.jpg">
<p>
<br /><br />
This may remind you of our <a href="http://py.processing.org/tutorials/2darray/">two dimensional arrays tutorial</a>. In fact, we'll need to use the same nested for loop technique. The difference is that, although we want to use for loops to think about the pixels in two dimensions, when we go to actually access the pixels, they live in a one dimensional array, and we have to apply the formula from the above illustration.
<br /><br />
Let's look at how it is done.
</p>
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-6/">Example: Setting Pixels according to their 2D location</a>
<br /><br />
<pre>
def setup():
size(200,200)
loadPixels()
changePixels()
def changePixels():
# Loop through every pixel column
for x in xrange(width):
# Loop through every pixel row
for y in xrange(height):
# Use the formula to find the 1D location
loc = x + y * width;
if (x % 2 == 0): # If we are an even column
pixels[loc] = color(255)
else: # If we are an odd column
pixels[loc] = color(0)
updatePixels()
</pre>
<h3>Intro To Image Processing</h3>
<p>
The previous section looked at examples that set pixel values according to an arbitrary calculation. We will now look at how we might set pixels according those found in an existing <a href="http://py.processing.org/reference/PImage.html">PImage</a> object. Here is some pseudo-code.
<br /><br />
(1) Load the image file into a PImage object<br />
(2) For each pixel in the PImage, retrieve the pixel's color and set the display pixel to that color.
<br /><br />
The <a href="http://py.processing.org/reference/PImage.html">PImage</a> class includes some useful fields that store data related to the image -- width, height, and pixels. Just as with our user-defined classes, we can access these fields via the dot syntax.
<br /><br />
</p>
<pre>
img = createImage(320,240,RGB) # Make a PImage object
print(img.width) # Yields 320
print(img.height) # Yields 240
img.pixels[0] = color(255,0,0) # Sets the first pixel of the image to red
</pre>
<p>
<br />
Access to these fields allows us to loop through all the pixels of an image and display them onscreen.
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-7/">Example: Displaying the pixels of an image</a>
<br /><br />
</p>
<pre>
# Display a 200x200 pixel image, pixel by pixel.
def setup():
global img
size(200, 200)
img = loadImage("sunflower.jpg")
def draw():
loadPixels()
# Since we are going to access the image's pixels too
img.loadPixels()
for y in xrange(height):
for x in xrange(width):
loc = x + y*width
# The functions red(), green(), and blue() pull out the
# 3 color components from a pixel.
r = red(img.pixels[loc])
g = green(img.pixels[loc])
b = blue(img.pixels[loc])
# Image Processing would go here
# If we were to change the RGB values, we would do it here,
# before setting the pixel in the display window.
# Set the display pixel to the image pixel
pixels[loc] = color(r,g,b)
updatePixels()
</pre>
<p>
<br />
Now, we could certainly come up with simplifications in order to merely display the image (for example, the nested loop is not required, not to mention that using the <a href="http://py.processing.org/reference/image.html">image()</a> function would allow us to skip all this pixel work entirely.) However, example 15-7 provides a basic framework for getting the red, green, and blue values for each pixel based on its spatial orientation (XY location); ultimately, this will allow us to develop more advanced image processing algorithms.
<br /><br />
Before we move on, I should stress that this example works because the display area has the same dimensions as the source image. If this were not the case, you would simply have to have two pixel location calculations, one for the source image and one for the display area.
<br /><br />
</p>
<pre>
imageLoc = x + y*img.width
displayLoc = x + y*width
</pre>
<h3>Our second image filter, making our own "tint"</h3>
<p>
Just a few paragraphs ago, we were enjoying a relaxing coding session, colorizing images and adding alpha transparency with the friendly <a href="http://py.processing.org/reference/tint.html">tint()</a>method. For basic filtering, this method did the trick. The pixel by pixel method, however, will allow us to develop custom algorithms for mathematically altering the colors of an image. Consider brightness -- brighter colors have higher values for their red, green, and blue components. It follows naturally that we can alter the brightness of an image by increasing or decreasing the color components of each pixel. In the next example, we dynamically increase or decrease those values based on the mouse's horizontal location. (Note, the next two examples include only the image processing loop itself, the rest of the code is assumed.)
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-8/">Example: Adjusting image brightness</a>
<br /><br />
</p>
<pre>
for x in xrange(img.width):
for y in xrange(img.height):
# Calculate the 1D pixel location
loc = x + y*img.width
# Get the R,G,B values from image
r = red(img.pixels[loc])
g = green(img.pixels[loc])
b = blue(img.pixels[loc])
# Change brightness according to the mouse here
adjustBrightness = (float(mouseX) / width) * 8.0
r *= adjustBrightness
g *= adjustBrightness
b *= adjustBrightness
# Constrain RGB to between 0-255
r = constrain(r,0,255)
g = constrain(g,0,255)
b = constrain(b,0,255)
# Make a new color and set pixel in the window
c = color(r,g,b)
pixels[loc] = c
</pre>
<p>
<br />
Since we are altering the image on a per pixel basis, all pixels need not be treated equally. For example, we can alter the brightness of each pixel according to its distance from the mouse.
<br /><br />
</p>
<img src="imgs/flashlight.jpg">
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-9/">Example: Adjusting image brightness based on pixel location</a>
<br /><br />
<pre>
for x in xrange(img.width):
for y in xrange(img.height):
# Calculate the 1D pixel location
loc = x + y*img.width
# Get the R,G,B values from image
r = red(img.pixels[loc])
g = green(img.pixels[loc])
b = blue(img.pixels[loc])
# Calculate an amount to change brightness
# based on proximity to the mouse
distance = dist(x,y,mouseX,mouseY)
adjustBrightness = (50-distance)/50
r *= adjustBrightness
g *= adjustBrightness
b *= adjustBrightness
# Constrain RGB to between 0-255
r = constrain(r,0,255)
g = constrain(g,0,255)
b = constrain(b,0,255)
# Make a new color and set pixel in the window
c = color(r,g,b)
pixels[loc] = c
</pre>
<h3>Writing to another PImage object's pixels</h3>
<p>
All of our image processing examples have read every pixel from a source image and written a new pixel to the Processing window directly. However, it's often more convenient to write the new pixels to a destination image (that you then display using the <a href="http://py.processing.org/reference/image.html">image()</a> function). We'll demonstrate this technique while looking at another simple pixel operation: threshold.
<br /><br />
A threshold filter displays each pixel of an image in only one of two states, black or white. That state is set according to a particular threshold value. If the pixel's brightness is greater than the threshold, we color the pixel white, less than, black. In the code below, we use an arbitrary threshold of 100.
</p>
<br /><br />
<img src="imgs/threshold.jpg">
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-10/">Example: Brightness Threshold</a>
<br /><br />
<pre>
def setup():
global source, destination
size(200, 200)
source = loadImage("sunflower.jpg")
# The destination image is created as a blank image the same size as the source.
destination = createImage(source.width, source.height, RGB)
def draw():
threshold = 127
# We are going to look at both image's pixels
source.loadPixels()
destination.loadPixels()
for x in xrange(source.width):
for y in xrange(source.height):
loc = x + y*source.width
# Test the brightness against the threshold
if (brightness(source.pixels[loc]) > threshold):
destination.pixels[loc] = color(255) # White
else:
destination.pixels[loc] = color(0) # Black
# We changed the pixels in destination
destination.updatePixels()
# Display the destination
image(destination,0,0)
</pre>
<p>
<br />
This particular functionality is available without per pixel processing as part of Processing's <a = href"http://py.processing.org/reference/filter.html">filter()</a> function. Understanding the lower level code, however, is crucial if you want to implement your own image processing algorithms, not available with <a = href"http://py.processing.org/reference/filter.html">filter()</a>.
<br /><br />
But if all you want to do is threshold, here is how:
<br />
</p>
<pre>// Draw the image
image(img,0,0)
# Filter the window with a threshold effect
# 0.5 means threshold is 50% brightness
filter(THRESHOLD,0.5)
</pre>
<h3>Level II: Pixel Group Processing</h3>
<p>
In previous examples, we've seen a one-to-one relationship between source pixels and destination pixels. To increase an image's brightness, we take one pixel from the source image, increase the RGB values, and display one pixel in the output window. In order to perform more advanced image processing functions, we must move beyond the one-to-one pixel paradigm into pixel group processing.
<br /><br />
Let's start by creating a new pixel out of a two pixels from a source image -- a pixel and its neighbor to the left.
<br /><br />
If we know the pixel is located at (x,y):
<br /><br />
</p>
<pre>
loc = x + y*img.width
pix = img.pixels[loc]
</pre>
<p>
<br />
Then its left neighbor is located at (x-1,y):
<br /><br />
</p>
<pre>
leftLoc = (x-1) + y*img.width
leftPix = img.pixels[leftLoc]
</pre>
<p>
<br />
We could then make a new color out of the difference between the pixel and its neighbor to the left.
<br /><br />
</p>
<pre>
diff = abs(brightness(pix) - brightness(leftPix))
pixels[loc] = color(diff)
</pre>
<p>
<br />
Here is the full algorithm:
<br><br />
<img src="imgs/edges.jpg">
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-12/">Example: Pixel neighbor differences (edges)</a>
<br /><br />
</p>
<pre>
# Since we are looking at left neighbors
# We skip the first column
for x in xrange(width):
for y in xrange(height):
# Pixel location and color
loc = x + y*img.width
pix = img.pixels[loc]
# Pixel to the left location and color
leftLoc = (x-1) + y*img.width
leftPix = img.pixels[leftLoc]
# New color is difference between pixel and left neighbor
diff = abs(brightness(pix) - brightness(leftPix))
pixels[loc] = color(diff)
</pre>
<p>
<br />
This example is a simple horizontal edge detection algorithm. When pixels differ greatly from their neighbors, they are most likely "edge" pixels. For example, think of a picture of white piece of paper on a black tabletop. The edges of that paper are where the colors are most different, where white meets black.
<br><br />
In the previous example, we looked at two pixels to find edges. More sophisticated algorithms, however, usually involve looking at many pixels at a time. After all, each pixel has 8 immediate neighbors: top left, top, top right, right, bottom right, bottom, bottom left, left.
<br><br />
</p>
<img src="imgs/neighbors.jpg">
<p>
<br><br />
These image processing algorithms are often referred to as a "spatial convolution." The process uses a weighted average of an input pixel and its neighbors to calculate an output pixel. In other words, that new pixel is a function of an area of pixels. Neighboring areas of different sizes can be employed, such as a 3x3 matrix, 5x5, etc.
<br><br />
Different combinations of weights for each pixel result in various effects. For example, we "sharpen" an image by subtracting the neighboring pixel values and increasing the center point pixel. A blur is achieved by taking the average of all neighboring pixels. (Note that the values in the convolution matrix add up to 1).
<br><br />
For example,
<br /><br />
</p>
<pre>
Sharpen:
-1 -1 -1
-1 9 -1
-1 -1 -1
Blur:
1/9 1/9 1/9
1/9 1/9 1/9
1/9 1/9 1/9
</pre>
<p>
<br />
Following is an example that performs a convolution using a 2D array (see Chapter 13, p. XX for a review of 2D arrays) to store the pixel weights of a 3x3 matrix. This example is probably the most advanced example we've encountered in this book so far since it involves so many elements (nested loops, 2D arrays, PImage pixels, and so on.)
<br><br />
<img src="imgs/sharpen.jpg">
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-13/">Example: Sharpen with Convolution</a>
<br /><br />
</p>
<pre>
w = 80
# It's possible to perform a convolution
# the image with different matrices
matrix = [ [ -1, -1, -1 ],
[ -1, 9, -1 ],
[ -1, -1, -1 ] ]
def setup():
global img
size(200, 200)
frameRate(30)
img = loadImage("sunflower.jpg")
def draw():
# We're only going to process a portion of the image
# so let's set the whole image as the background first
image(img,0,0)
# Where is the small rectangle we will process
xstart = constrain(mouseX-w/2,0,img.width)
ystart = constrain(mouseY-w/2,0,img.height)
xend = constrain(mouseX+w/2,0,img.width)
yend = constrain(mouseY+w/2,0,img.height)
matrixsize = 3
loadPixels()
# Begin our loop for every pixel
for x in xrange(xstart,xend):
for y in xrange(ystart,yend):
# Each pixel location (x,y) gets passed into a function called convolution()
# which returns a new color value to be displayed.
c = convolution(x,y,matrix,matrixsize,img)
loc = x + y*img.width
pixels[loc] = c
updatePixels()
stroke(0)
noFill()
rect(xstart,ystart,w,w)
def convolution(x, y, matrix, matrixsize, img):
rtotal = 0.0
gtotal = 0.0
btotal = 0.0
offset = matrixsize / 2
# Loop through convolution matrix
for i in xrange(matrixsize):
for j in xrange(matrixsize):
# What pixel are we testing
xloc = x+i-offset
yloc = y+j-offset
loc = xloc + img.width*yloc
# Make sure we have not walked off the edge of the pixel array
loc = constrain(loc,0,len(img.pixels)-1)
# Calculate the convolution
# We sum all the neighboring pixels multiplied by the convolution matrix values.
rtotal += (red(img.pixels[loc]) * matrix[i][j])
gtotal += (green(img.pixels[loc]) * matrix[i][j])
btotal += (blue(img.pixels[loc]) * matrix[i][j])
# Make sure RGB is within range
rtotal = constrain(rtotal,0,255)
gtotal = constrain(gtotal,0,255)
btotal = constrain(btotal,0,255)
# Return the resulting color
return color(rtotal,gtotal,btotal)
</pre>
<h3>Visualizing the Image</h3>
<p>
You may be thinking: "Gosh, this is all very interesting, but seriously, when I want to blur an image or change its brightness, do I really need to write code? I mean, can't I use Photoshop?" Indeed, what we have achieved here is an merely an introductory understanding of what highly skilled programmers at Adobe do. The power of Processing, however, is the potential for real-time, interactive graphics applications. There is no need for us to live within the confines of "pixel point" and "pixel group" processing.
<br /><br />
Following are two examples of algorithms for drawing processing shapes. Instead of coloring the shapes randomly or with hard-coded values as we have in the past, we select colors from pixels inside of a <a href="http://py.processing.org/reference/PImage.html">PImage</a> object. The image itself is never displayed; rather, it serves as a database of information that we can exploit for a multitude of creative pursuits.
<br /><br />
In this first example, for every cycle through draw(), we fill one ellipse at a random location onscreen with a color taken from its corresponding location in the source image. The result is a basic "pointillist-like" effect:
<br /><br />
</p>
<img src="imgs/pointillism.jpg">
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-14/">Example: "Pointillism"</a>
<br /><br />
<pre>
pointillize = 16
def setup():
global img
size(200,200)
img = loadImage("sunflower.jpg")
background(0)
smooth()
def draw():
global img, pointillize
# Pick a random point
x = int(random(img.width))
y = int(random(img.height))
loc = x + y*img.width
# Look up the RGB color in the source image
loadPixels()
r = red(img.pixels[loc])
g = green(img.pixels[loc])
b = blue(img.pixels[loc])
noStroke()
# Draw an ellipse at that location with that color
fill(r,g,b,100)
ellipse(x,y,pointillize,pointillize)
</pre>
<p>
<br />
In this next example, we take the data from a two-dimensional image and using the 3D translation techniques described in chapter 14, render a rectangle for each pixel in three-dimensional space. The z location is determined by the brightness of the color. Brighter colors appear closer to the viewer and darker ones farther away.
</p>
<br /><br />
<img src="imgs/explode1.jpg"> <img src="imgs/explode2.jpg"> <img src="imgs/explode3.jpg">
<br /><br />
<a href="http://www.learningprocessing.com/examples/chapter-15/example-15-15/">Example: 2D image mapped to 3D</a>
<br /><br />
<pre>
cellsize = 2 # Dimensions of each cell in the grid
def setup():
global img, cols, rows, cellsize
size(200, 200, P3D)
img = loadImage("sunflower.jpg") # Load the source image
cols = width/cellsize # Calculate number of columns
rows = height/cellsize # Calculate number of rows
def draw():
global img, cols, rows, cellsize
background(0)
loadPixels()
# Begin loop for columns
for i in xrange(cols):
# Begin loop for rows
for j in range(rows):
x = i*cellsize + cellsize/2 # x position
y = j*cellsize + cellsize/2 # y position
loc = x + y*width # Pixel array location
c = img.pixels[loc] # Grab the color
# Calculate a z position as a function of mouseX and pixel brightness
z = (mouseX/(float(width))) * brightness(img.pixels[loc]) - 100.0
# Translate to the location, set fill and stroke, and draw the rect
pushMatrix()
translate(x,y,z)
fill(c)
noStroke()
rectMode(CENTER)
rect(0,0,cellsize,cellsize)
popMatrix()
</pre>
<p> </p>
<p class="license">This tutorial is for Python Mode of Processing version 2+. If you see any errors or have comments, please <a href="https://github.com/jdf/processing-py-site/issues?state=open">let us know</a>. This tutorial is from the book, <a href="http://www.processing.org/learning/books/#shiffman">Learning Processing</a>, by Daniel Shiffman, published by Morgan Kaufmann Publishers, Copyright © 2008 Elsevier Inc. All rights reserved.</p>
</td>
</tr>
</table>
</p>