A Little Image Processing

What can we do with an image?

The following are some examples of things that can be done with images. A few notes are included, mostly the code speaks for itself and details may be found in the Wolfram Documentation.

ImagePartition & ImageAssemble

rose=;

ImageDimensions[rose]

rose=ImageResize[rose,500]

Break the image up into blocks of size 50 pixels. The result is a list of lists. Each sublist is a row starting from the top.

ImagePartition[rose,50]

Grid[ImagePartition[rose,50]]

Randomly reorder the rows.

RandomSample[ImagePartition[rose,50]]

Put the image back together with a new ordering of the rows. It’s a new ordering of the rose!

ImageAssemble[RandomSample[ImagePartition[rose,50]]]

Randomly reorder the blocks within each row. Everything stays within its original row. (Note: This is clearer to see if the top of the image has features or colors that are different than the bottom.)

ImageAssemble[RandomSample/@ImagePartition[rose,50]]

All of the blocks in one row.

ImageAssemble[Flatten[ImagePartition[rose,50]]]

Randomly reorder all of the blocks, then partition the list into rows for reassembly.

ImageAssemble[Partition[RandomSample[Flatten[ImagePartition[rose,50]]],500/50]]

Same as above, but with larger blocks.

ImageAssemble[Partition[RandomSample[Flatten[ImagePartition[rose,100]]],500/100]]

Altering Image

Blur[rose,10]

Sharpen[rose,10]

Sharpen[Blur[rose,10],100]

Dilation[rose,10]

Lighter[rose]

Darker[rose]

ImageData

Images are made of pixels and each pixel is determined by numbers. In this case each pixel has an “RGB” value, a list with numbers between 0 and 1 representing the amount of red, green, and blue light to display. ImageData shows the list of all of these values. Below displays the first 10 pixels in the top row.

ImageData[rose][[1,1;;10]]

When adjustments are made to the image, these numbers change. For example, when we make the image darker, the numbers are are lowered.

ImageData[Darker[rose]][[1,1;;10]]

Applying Lighter to an image raises the numbers. A value of {0,0,0} would be no light (black) and {1,1,1} would be all light (white).

ImageData[Lighter[rose]][[1,1;;10]]

Since the image is just lists of numbers, we can adjust the numbers directly.

{rose,rose*0.5,rose*1.5}

{rose,rose+0.5,rose-0.5}

Sqrt[rose]

Cos[rose]

More Adjustments

Adjust contrast

ImageAdjust[rose,5]

Adjust brightness as a second element after contrast. Display original with the new one.

{rose,ImageAdjust[rose,{0,1}]}

Adjust both contrast and brightness.

{rose,ImageAdjust[rose,{1,1}]}

There are 3 channels because the data is only RGB.

ImageChannels[rose]

The alpha channel is a fourth value that indicates transparency. Setting that to 0.25 fades the image, but it is actually now somewhat transparent if placed on another.

roseA=SetAlphaChannel[rose,0.25]

Notice for this image, there are now four values for each pixel.

ImageData[roseA][[1,1;;5]]

Color

Change the image to grayscale.

ColorConvert[rose,"Grayscale"]

Notice that now every pixel has only one value.

ImageData[ColorConvert[rose,"Grayscale"]][[1,1;;5]]

The original rose is in “RGB” colorspace.

ImageColorSpace[rose]

We can separate the image into its channels for Red, Green, and Blue. The first image represents the value of Red for each pixel with 0 as black for “none” and 1 for white for “all.” The second image is the same for Green and the third for Blue.

ColorSeparate[rose]

ColorCombine puts the channel information back together according to “RGB” values.

ColorCombine[ColorSeparate[rose]]

We can randomly reorder the color separations and then put it back together. I recommend evaluating this a couple of times. So it may, for example, be treating the Green information in the Red channel.

ColorCombine[RandomSample[ColorSeparate[rose]]]

There are six possible permutations.

Permutations[{r,g,b}]

We can separate the colors and then show all of the possible permutations.

Permutations[ColorSeparate[rose]]

If we map ColorCombine across this list, we will generate each of the possible images.

ColorCombine/@Permutations[ColorSeparate[rose]]

An image may be converted to a different colorspace, such as “HSB” for “Hue, Saturation, Brightness.” The image data is still three values for each pixel, but now the numbers represent these items rather than Red, Green, and Blue.

ColorSeparate[ColorConvert[rose,"HSB"]]

ColorCombine/@Permutations[ColorSeparate[ColorConvert[rose,"HSB"]]]

Why do none of these look like the original? What did it do? ColorCombine uses the input as “RGB” by default, so it treated the “HSB” separations as if they were “RGB.” We can tell ColorCombine to use “HSB” as below.

ColorCombine[#,"HSB"]&/@Permutations[ColorSeparate[ColorConvert[rose,"HSB"]]]

There are functions for working with colors directly.

ColorReplace[rose,Red->Blue]

Since it was looking for exactly Red (RGBColor[1,1,1]), this is only a small part of the rose. The red in the rose is slightly different.

DominantColors[rose]

I copy/pasted the brighter red from this list into the ColorReplace.

ColorReplace[rose,->Blue]

ColorReplace[rose,->Orange]

bouquet=;

ColorReplace[bouquet,Red->Orange]

ImageRecolor

ImageRecolor is a higher-level function that works to replace a color in a more natural looking way within the image.

ImageRecolor[rose,Red->Blue]

Compare this to ColorReplace. ColorReplace replaces exact Red with exact Blue. Note that ImageRecolor above, tries to match the values (lightness and darkness) of the color.

ColorReplace[rose,Red->Blue]

Since exact Red is not common within the rose, we can use ImageRecolor and ColorsNear to adjust which colors close to exactly Red will change to Blue.

ImageRecolor[rose,ColorsNear[Red,0.3]->Blue]

Or let’s pull out the most dominant red color directly. Copy/paste one of the red color swatches from the list and use that with ImageRecolor. (I use the brighter red swatch below.)

DominantColors[rose]

ImageRecolor[rose,->Blue]

ImageRecolor[rose,->Orange]

ImageRecolor[rose,->Purple]

Let’s put this together to see several at once.

Grid[Partition[
ImageRecolor[rose,->#]&/@{Red,Orange,Yellow,Green,Blue,Purple,Pink,White,Cyan},
3],Spacings->{0,0}]

ImageCompose

ImageDimensions[bouquet]

bouquet=ImageResize[bouquet,185]

ImageCompose places one image on top of another. The rose picture with transparency is on top. The composed image dimensions are determined by the first image.

ImageCompose[bouquet,SetAlphaChannel[rose,0.6]]

roseA=SetAlphaChannel[rose,0.25]

ImageCompose[roseA,bouquet]

RemoveBackground[bouquet,White]

ImageCompose[roseA,RemoveBackground[bouquet,White]]

ImageCompose[roseA,RemoveBackground[bouquet,White],Scaled[{0.8,0.23}]]

ImageEffect

ImageEffect[rose,"Charcoal"]

ImageEffect[rose,{"Charcoal",5}]

ImageEffect[rose,"OilPainting"]

ImageEffect[rose,{"OilPainting",10}]

ImageEffect[rose,"EdgeStylization"]

ImageEffect[rose,{"EdgeStylization",3}]

ImageEffect[rose,"Decolorization"]

ImageEffect[rose,"Jitter"]

ImageEffect[rose,{"MotionBlur",30,Pi/4}]

ImageEffect[ImageEffect[rose,{"Jitter",10}],{"MotionBlur",30,Pi/4}]

ImageEffect[rose,{"Vignette",175}]

ImageEffect[rose,{"FadedFrame",50}]

ImageEffect[rose,{"Comics",{0.15,0.2}}]

I have not seen “NestedComposition” before, so I played around with it a little using the Wolfram Documentation as a guide.

ImageEffect[rose,"NestedComposition"]

ImageEffect[rose,{"NestedComposition",20}]

ImageEffect[rose,{"NestedComposition",20,.9,5Degree}]

ImageEffect[rose,{"NestedComposition",5,0.9,0}]

ImageEffect[rose,{"NestedComposition",5,0.7,10 Degree,Scaled[{0.6,0.6}]}]

ImageEffect[rose,{"NestedComposition",8,0.7,15 Degree,Scaled[{0.65,0.58}]}]

Putting Some Things Together

My daughter peeking over my shoulder once again asked me to “Make the rose look completely different.”

Blur[ColorReplace[ImageEffect[rose,{"NestedComposition",8,0.7,15 Degree,Scaled[{0.65,0.58}]}],->Orange],10]

ImageEffect[Blur[ColorReplace[ImageEffect[rose,{"NestedComposition",8,0.7,15 Degree,Scaled[{0.65,0.58}]}],->Orange],10],{"EdgeStylization",3}]

Was that a rose?

Challenge: Effects on Partitions

Can we apply an effect randomly to individual partitions of an image?

ImageAssemble[Partition[Map[RandomChoice[{Identity[#],Darker[#,0.5]}]&,Flatten[ImagePartition[rose,50]]],500/50]]

ImageAssemble[Partition[Map[RandomChoice[{Identity[#],ImageEffect[#,{"EdgeStylization",3}]}]&,Flatten[ImagePartition[rose,100]]],500/100]]

ImageAssemble[Partition[Map[RandomChoice[{Identity[#],ImageEffect[#,"NestedComposition"]}]&,Flatten[ImagePartition[rose,100]]],500/100]]

ImageAssemble[Partition[Map[
RandomChoice[{Identity[#],ImageEffect[#,"ColorBoosting"],ImageEffect[#,"Sepia"]}]&,
Flatten[ImagePartition[rose,100]]],500/100]]

Challenge: RGB Adjuster

Can I create something that will let me adjust the values for Red, Green, and Blue within the image?

ColorSeparate[rose]

ColorCombine[ColorSeparate[rose]+{0.2,0.2,0.2}]

rose+0.2

ColorCombine[ColorSeparate[rose]+{0.8,0.2,0.2}]

ColorCombine[ColorSeparate[rose]+{0,0,0.8}]

Manipulate[ColorCombine[ColorSeparate[rose]+rgb],{{rgb,{0,0,0},"RGB"},{-1,-1,-1},{1,1,1}}]

Adapt this for Hue, Saturation, and Brightness.

Manipulate[ColorCombine[ColorSeparate[rose,"HSB"]+hsb,"HSB"],{{hsb,{0,0,0},"HSB"},{0,-1,-1},{1,1,1},Appearance->"Labeled"}]

Both at the same time?

Manipulate[ColorCombine[ColorSeparate[
ColorCombine[ColorSeparate[rose,"RGB"]+rgb,"RGB"],"HSB"]+hsb,"HSB"],
{{hsb,{0,0,0},"HSB"},{0,-1,-1},{1,1,1},Appearance->"Labeled"},
{{rgb,{0,0,0},"RGB"},{-1,-1,-1},{1,1,1},Appearance->"Labeled"}]

Manipulate[ColorCombine[ColorSeparate[
ColorCombine[ColorSeparate[rose,"RGB"]+rgb,"RGB"],"HSB"]+hsb,"HSB"],
Row[{Control@{{hsb,{0,0,0},"HSB"},{0,-1,-1},{1,1,1},Appearance->"Labeled"},Button["reset",hsb={0,0,0}]}],
Row[{Control@{{rgb,{0,0,0},"RGB"},{-1,-1,-1},{1,1,1},Appearance->"Labeled"},Button["reset",rgb={0,0,0}]}]]

Aside: Finding the adjustment values for an image

While playing with the HSB Manipulate above I created an image that caught my eye, so I copy/pasted it into a new cell. Later I found that I could not figure out the values I used to make the image.

yellowrose=;

Could I figure it out looking a the values for the pixels?

ImageData[yellowrose][[1,1;;10]]

And comparing that to the original?

ImageData[rose][[1,1;;10]]

Since my Manipulate just adds values, subtraction should undo it, right?

ImageData[yellowrose][[1,1;;10]]-ImageData[rose][[1,1;;10]]

Oh! The original rose is RGB and the adjustments were HSB, so we need to have everything in HSB.

ImageData[yellowrose][[1,1;;10]]-ImageData[ColorConvert[rose,"HSB"]][[1,1;;10]]

Those must have been the values I used. Let’s check by directly adding the values to the original rose in HSB and comparing to the image I had saved.

GraphicsRow[{yellowrose,ColorCombine[ColorSeparate[rose,"HSB"]+{0.19,0.36,0.3},"HSB"]}]

Image Credits

A Google Images search of “rose” led me to the two images that I used.

rose: https://www.homedepot.com/p/Mea-Nursery-FRAGRANT-Red-Mister-Lincoln-Rose-62014/304937447

bouquet: https://www.floristrochelleil.com/product/ro00707/24-radiant-roses

Abby Brown
CT@TP: Computational Thinking at Torrey Pines High School
www.abbymath.com/CT
San Diego, CA
March 2019 (updated with ImageRecolor January 2021)