Exploration of the new PV-WAVE Photo functions (Part 2)

on Mar 1, 17 • by Ed Stewart • with No Comments

Part 2 in my exploration of the new PV-WAVE Photo function, we'll answer the question "Now that I can display a photo, what can I do with it?"...

Home » Data Visualization » Exploration of the new PV-WAVE Photo functions (Part 2)

In Part 1 of this series, I cataloged my initial exploration of the Photo functions introduced in PV-WAVE 2016.1. That part included some helper functions that wrap existing PV-WAVE routines that work on array-based data for the new photo associative arrays. This second part moves on to the next step: Now that I can display a photo, what can I do with it?


Computing a histogram for each color channel as well as the luminosity is probably the most common technique used to quantify an image. For many editing tools, this information can be key when trying to identify the starting point to improve the quality of an image. PV-WAVE includes a powerful function for computing histograms of n-dimensional data with HISTN.

Computing the histogram for a given color channel is fairly straightforward to accomplish for a 24-bit image by extracting the channel and calling HISTN with the number of bins set to 256. In combining all three color channels, standard practice is to use a measure of luminosity rather than simply summing or averaging the RGB channels. There are several popular methods for this and the function implements various options using a keyword.

The most popular standard is for “relative luminance” as computed with:

Where L is the luminosity of a given pixel, and R, G, and B are the color values at that pixel. Alternatively, one may prefer perceived luminosity as standardized by and defined by:

A slightly slower version of this equation is used in some commercial photo editing tools as determined independently by a user looking to create his own brightness scale with:

The differences are subtle, and each is dominated by the green component. Additional equations could easily be added if there is a particular methodology required.

Wrapping HISTN is very straightforward, but I found that I did not need to dig deeply into the results yet, but rather wanted to quickly display all the information. For this use, I added a Showplot keyword to quickly display the results, combining all four output vectors into a single image with the help of the PLOT_HISTOGRAM procedure.

Using the same picture of some trees at sunset from part 1 of this series, the process of creating and displaying a set of histograms is quite short:

WAVE> t = PHOTO_READ(‘c:\dev\data\trees.jpg’)
WAVE> h = PHOTO_HISTN(t, /Showplot)

These commands generate the image shown below.

Inverted colors

On the topic of color channels, another frequently used function in modifying photos is to invert the colors. A full inversion results in the familiar white to black and full-bright RGB to full-bright CMY. However, it is straightforward to invert individual channels as well, yielding some artistic looking results.

For this utility, there is no PV-WAVE function to wrap, but rather the calculation is just a matter of subtracting the current color scale from 255. The PHOTO_INVERT function includes keywords for each of red, green, and blue to allow inverting separate channels easily. Passing all or none of the keywords will invert every channel.

Using my trees photograph as a base, I was able to create an interesting image that I liked so much I printed it out and have it on my desk. The idea was to invert each channel individually and in pairs to cover all combinations, and then combine them in the same image. The following code is optimized for readability rather than optimized for efficient use of variables and memory within PV-WAVE.

To begin, we’ll load in the source photograph, rotate and resize it (as was covered in part 1 and using the functions written there). To enable easier copy & paste into a PV-WAVE window for those that wish to follow along, the WAVE> prompt is not included in the command list.

t = PHOTO_READ('c:\dev\data\trees.jpg')
r = PHOTO_ROTATE(t, 3)
z = PHOTO_RESIZE(r, h=500)

The resized image is 375 x 500 pixels; these dimensions are needed when creating the concatenated final image. For the highest image quality, ideally a resize would be avoided or saved until the very last step; however, I’ve resized here for convenience and simplicity. The next step is to invert each combination of color channels. The CMY nomenclature was used for ease because “cyan” is essentially “not red” in RGB color space.

zr = PHOTO_INVERT(z, /R)
zg = PHOTO_INVERT(z, /G)
zb = PHOTO_INVERT(z, /B)
zc = PHOTO_INVERT(z, /G, /B)
zm = PHOTO_INVERT(z, /R, /B)
zy = PHOTO_INVERT(z, /R, /G)

Next, flip the full inverse and CMY inverted images using Direction = 7 in ROTATE:

zir = PHOTO_ROTATE(zi, 7)
zcr = PHOTO_ROTATE(zc, 7)
zmr = PHOTO_ROTATE(zm, 7)
zyr = PHOTO_ROTATE(zy, 7)

To combine all eight images, copy each pixel array into portions of a new larger byte array to hold them all:

ww = 375*4
hh = 500*2
xx = BYTARR(ww, hh, 3, /Nozero)
xx(0,hh/2,0) = zr('pixels')
xx(ww/4,hh/2,0) = zg('pixels')
xx(ww/2,hh/2,0) = zb('pixels')
xx(3*ww/4,hh/2,0) = z('pixels')
xx(0,0,0) = zcr('pixels')
xx(ww/4,0,0) = zmr('pixels')
xx(ww/2,0,0) = zyr('pixels')
xx(3*ww/4,0,0) = zir('pixels')

From the pixel array, a new image can easily be created and saved to disk or displayed:

status = PHOTO_WRITE('c:\dev\data\treesinv.jpg', pp)

The final image is presented below.

As an aside, as I worked through the pixel layout part of this example, I used TVSCL to make sure the pixel arrays were properly positioned. Interestingly, the way the final image appears using this function seems to indicate that the inverted red and inverted “not red” should be swapped for consistency. The logical consistency part of me wants to use the RGB-CMY layout as presented, but the visual consistency part wants to use CGB-RMY instead. I will leave it to you to decide which you like best.

While working through this post with a variety of test images, I found cases where techniques for 24-bit RGB images and 8-bit grayscale images all work fine, but for 8-bit images with a color table, things go awry. Since the color information is essentially decoupled from the pixel values (the values are just lookup indices into the table), tasks like inverting with 255-v are meaningless. For such images, it would be easier to use a true 24-bit image, even if the image had been quantized down. To un-quantize the image, essentially the reverse process of PHOTO_COLOR_QUANT, I wrote an additional tool called PHOTO_8to24 which may be helpful to others in their explorations.


The PHOTO_HISTN and PHOTO_INVERT functions are included in their entirety in GitLab. In the next part of this series, I will explore edge detection and sharpening based on some array mathematics and other PV-WAVE standard library functions.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top