[This article was first published on Mark H. White II, PhD, and kindly contributed to R-bloggers], (You can report a problem about the content of this page here) Want to share your content on R-Blogger? Click here if you have a blog, or click here if you don’t.

i started taking photos earlier this year. And as someone who likes to think about probability, statistics, chance, randomness and R programming, I started thinking about ways to apply probabilistic programming to photography. This is my first attempt.

I’m going to use one shot that I particularly like. This is a tower in Kansas City, Missouri on the 47th between Wyndotte and Baltimore—as seen from the roof of the parking garage above the Cheesecake Factory:

Through fumbling around for a while, I developed an, uh, “algorithm” for, of course, let’s call it, for disturbing and abstracting a picture. At a high level, what it is doing is changing the location of pixels according to a uniform distribution and changing colors according to a normal distribution.

The code for the following steps is found at the bottom of the page and linked to my GitHub.

Step

Present the picture as a five-column data.frame, where each row is a pixel: two columns for the x and y locations, then three columns for the red, green, and blue values that determine the color of that pixel.

Draw a number from an even distribution tied at .25 and .75. This is what I would call “jumble probability”.

For each pixel, draw on a “jumble probability” with p set from the Bernoulli distribution.

Take all the pixels that drew 1 in step 3 and make them your set. Then “jumble” them: move them around, rearranging them randomly in the xy plane.

All red, green and blue values are normalized from 0 to 1 in the Imager package. And we want to extend these a bit, so:

Take three draws from a normal distribution with a mean of 0 and a standard deviation of .1.

From this distribution: Add the first draw to the red value, the second draw to the green value, and the third draw to the blue value.

Wherever this leads to a value greater than 1, make them 1; Whenever this leads to values less than 0, make them 0. These three values make up the new color of the pixel.

With high-resolution images, you have tons of pixels. I had a data.frame with 24,000,000 rows in my picture. Trying to plot all of this took a lot of computing power – and frankly, I didn’t want to wait that long to see the images. So, given this practical consideration, let’s add another abstraction:

Draw a number, let’s call it “pixel count”, from a uniform distribution limited to 1,000 and 1,000,000. (Round to the nearest integer.)

Filter randomly for a subset of “pixel count” pixels.

This creates some white space, so I made each pixel a square point in ggplot2 and randomly varied the size:

Create a number from a uniform distribution bounded on 5 and 30, round again to the nearest integer, and use it as the size parameter in geom_point().

Create a scatterplot with each line represented as a square.

Result

I did this 100 times and used ImageMagick in terminal to create a .gif (see code below) that shows 10 of these images every second. This gives us an interesting look at the probability that it applies to an underlying image:

This is where I talk about how memory is reconstructed and abstracted and how time distorts our memories. So each time we recall a memory, it is slightly different in random ways. And that’s what this passage shows. We never get the whole image back, just the fragmented pieces. Or, maybe this is where I talk about how we design all of our life plans—but life is chaos and random and stochastic, so this piece shows that even though we can control the general direction, our Life goes on, we don’t end up largely because of the randomness inherent in human existence. Or is this where I say I thought it was a fun .gif to make; Read as much as you can in it.

R code library (imager) library (tidyverse) plot_point = function(img, n, …) { ggplot(slice_sample(img, n = n), aes(x, y)) + geom_point( aes(color = hex) , …) + scale_color_identity() + scale_y_reverse() + theme_void() } img <- load.image("20221112_DSC_0068_TP.JPG") # load image in dim <- dim(img)[1:2] # get dimensions for export # change data frame img_dat <- img%>% as.data.frame(wide = “c”)%>% mutate(hex = rgb(c.1, c.2, c.3) , xy = paste(x, y, sep = “_”)) # Create an “algorithm”, set it up 100 times. seed(1839) for (i in seq_len(100)) { cat(“starting” , i, “\n”) # rumble with probability p_jumble <- runif(1, .25, .75) # find that which points jumble to_jumble <- as.logic(rbinom(nrow(img_dat), 1, p_jumble) ) # order jumbled, brb jumbled <- order (runif(sum(to_jumble)))) # add some error to each color column # then the hex value c_err <- rnorm(3, 0, .1) img_dat_edit <- पर जाएं। img_dat%>% mutate(# needs to be created between 0 and 1 c.1 = c.1 + c_err[1]c.1 = ifelse(c.1 > 1, 1, c.1), c.1 = ifelse(c.1 <0, 0, c.1), c.2 = c.2 + c_err[2]c.2 = ifelse(c.2 > 1, 1, c.2), c.2 = ifelse(c.2 <0, 0, c.2), c.3 = c.3 + c_err[3]c.3 = ifelse(c.3 > 1, 1, c.3), c.3 = ifelse(c.3 < 0, 0, c.3), hex = rgb(c.1, c.2 , c.3) ) # then combine the colors use jumble for img_dat_edit$hex[to_jumble] <- img_dat_edit$hex[jumbled] # select n random pixels of random shape n <- round(runif(1, 1000, 1000000)) shape = round(runif(1, 5, 30)) # plot and save p <- plot_point(img_dat_edit, n, shape = "square", shape = shape) ggsave(paste0("plaza/plaza_iter_", i, ".png"), p, width = dims[1]height = dim[2]units = "px") }

There is a way to create a .gif using the magic package for R, but it was creating a really huge file and taking forever, so I used the built-in imagemagick package in the command line.

convert – resize 15% – delay 10 -loop 0 – dispose last plaza/*.png plaza.gif

related