Rainbowing a set of pictures

[This article was first published on Maëlle, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

I’ve now down a few collages from R using magick: the faces of #rstats Twitter, We R-Ladies with Lucy D’Agostino McGowan, and a holiday card for R-Ladies. The faces of #rstats Twitter and holiday card collages were arranged at random, while the We R-Ladies one was a mosaic forming the R-Ladies logo. I got the idea to up my collage skills by trying to learn how to arrange pics by their main colour, like a rainbow. The verb rainbow doesn’t exist, and “rainbowing” doesn’t mean ordering by colour, but I didn’t let this stop me.

It was the occasion to grab some useful knowledge about colours, not useless for someone who did not even know about Pantone’s Colors of the Year a few weeks ago…

This post has nothing to do with Kesha’s new album. However, you can listen to it while reading since it’s so good, but maybe switch to something older from her when I use “$”.

Getting some pics to play with

The first pictures I tried to arrange were all the pictures ever posted by R-Ladies local chapters on their Twitter account. While it was fun to grab them all, it was not very interesting to play with them as so many of them were pictures of screens. I therefore grabbed “nature” pictures from Pexels using the same method [as when creating the Bubblegum Puppies] following this Stack Overflow thread. I chose “nature” as a keyword because 1) it lead to many hits 2) it offered a good variety of colours.

<span class="n">library</span><span class="p">(</span><span class="s2">"rvest"</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"RSelenium"</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"magrittr"</span><span class="p">)</span><span class="w">

</span><span class="n">rD</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">rsDriver</span><span class="p">()</span><span class="w">
</span><span class="n">remDr</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">rD</span><span class="p">[[</span><span class="s2">"client"</span><span class="p">]]</span><span class="w">


</span><span class="c1"># open the webpage</span><span class="w">
</span><span class="n">remDr</span><span class="o">$</span><span class="n">navigate</span><span class="p">(</span><span class="s2">"https://www.pexels.com/search/nature/"</span><span class="p">)</span><span class="w">

</span><span class="c1"># scroll down</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">130</span><span class="p">){</span><span class="w">      
  </span><span class="n">remDr</span><span class="o">$</span><span class="n">executeScript</span><span class="p">(</span><span class="n">paste</span><span class="p">(</span><span class="s2">"scroll(0,"</span><span class="p">,</span><span class="n">i</span><span class="o">*</span><span class="m">10000</span><span class="p">,</span><span class="s2">");"</span><span class="p">),</span><span class="w">
                      </span><span class="n">args</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="s2">"dummy"</span><span class="p">))</span><span class="w">
  </span><span class="c1"># be nice and wait</span><span class="w">
  </span><span class="n">Sys.sleep</span><span class="p">(</span><span class="m">1</span><span class="p">)</span><span class="w">    
</span><span class="p">}</span><span class="w">

</span><span class="c1"># https://www.pexels.com/faq/</span><span class="w">

</span><span class="n">page_content</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">remDr</span><span class="o">$</span><span class="n">getPageSource</span><span class="p">()</span><span class="w"> 
</span><span class="n">remDr</span><span class="o">$</span><span class="n">close</span><span class="p">()</span><span class="w">

</span><span class="n">get_link_from_src</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">node</span><span class="p">){</span><span class="w">
  </span><span class="n">xml2</span><span class="o">::</span><span class="n">xml_attrs</span><span class="p">(</span><span class="n">node</span><span class="p">)[</span><span class="s2">"src"</span><span class="p">]</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="nf">as.character</span><span class="p">()</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">stringr</span><span class="o">::</span><span class="n">str_replace</span><span class="p">(</span><span class="s2">"\\?h.*"</span><span class="p">,</span><span class="w"> </span><span class="s2">""</span><span class="p">)</span><span class="w">
  
</span><span class="p">}</span><span class="w">

</span><span class="n">xtract_pic_links</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">source</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">css</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="s1">'.photo-item__img'</span><span class="w">
  </span><span class="n">read_html</span><span class="p">(</span><span class="n">source</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">html_nodes</span><span class="p">(</span><span class="n">css</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">purrr</span><span class="o">::</span><span class="n">map_chr</span><span class="p">(</span><span class="n">get_link_from_src</span><span class="p">)</span><span class="w">    
</span><span class="p">}</span><span class="w">

</span><span class="n">links</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">xtract_pic_links</span><span class="p">(</span><span class="n">page_content</span><span class="p">)</span><span class="w">
</span><span class="n">links</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">links</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">1400</span><span class="p">]</span><span class="w">

</span><span class="c1"># save</span><span class="w">
</span><span class="n">dir.create</span><span class="p">(</span><span class="s2">"nature"</span><span class="p">)</span><span class="w">
</span><span class="n">save_pic</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">url</span><span class="p">){</span><span class="w">
  </span><span class="n">Sys.sleep</span><span class="p">(</span><span class="m">1</span><span class="p">)</span><span class="w">
  </span><span class="n">name</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">stringr</span><span class="o">::</span><span class="n">str_replace</span><span class="p">(</span><span class="n">url</span><span class="p">,</span><span class="w"> </span><span class="s2">".*\\/"</span><span class="p">,</span><span class="w"> </span><span class="s2">""</span><span class="p">)</span><span class="w">
  
  </span><span class="n">try</span><span class="p">(</span><span class="n">magick</span><span class="o">::</span><span class="n">image_read</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">magick</span><span class="o">::</span><span class="n">image_write</span><span class="p">(</span><span class="n">paste0</span><span class="p">(</span><span class="s2">"nature/"</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">)),</span><span class="w">
      </span><span class="n">silent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">purrr</span><span class="o">::</span><span class="n">walk</span><span class="p">(</span><span class="n">links</span><span class="p">,</span><span class="w"> </span><span class="n">save_pic</span><span class="p">)</span><span class="w">

</span>

Extracting the main colour and making pics size-compatible

In the following code, I extracted the main colour from each pic using Russel Dinnage’s method as presented in this blog post from David Smith. Before that I had to install two packages from Github, rblocks and rPlotter.

This code also serves another role: since I wanted to paste pics together at some point, I decided to make them all of the same dimensions by adding a border with magick. I had learnt how to do that when preparing R-Ladies Global holiday card, but this time instead of using the same colour every time (R-Ladies’ official purple), I used the main colour I’d just extracted. The most important points to make a picture a square are to know magick::image_info gives you the height and width of an image… and to somehow understand geometry which was embarrassingly a hurdle when I did that.

The code to extract colours didn’t work in a few cases which I did not investigate a lot: I had downloaded more pics than what I needed because I had experienced the issue when working with R-Ladies meetups pics, and had seen it was because of seemingly bicolor pics.

<span class="n">dir.create</span><span class="p">(</span><span class="s2">"formatted_pics"</span><span class="p">)</span><span class="w">

</span><span class="n">format_image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">path</span><span class="p">){</span><span class="w">
  </span><span class="n">image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_read</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="w">
  </span><span class="n">info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_info</span><span class="p">(</span><span class="n">image</span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># find in which direction I need to add pixels</span><span class="w">
  </span><span class="c1"># to make this a square</span><span class="w">
  </span><span class="n">direction</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">info</span><span class="o">$</span><span class="n">height</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="n">info</span><span class="o">$</span><span class="n">width</span><span class="p">,</span><span class="w">
                      </span><span class="s2">"height"</span><span class="p">,</span><span class="w"> </span><span class="s2">"width"</span><span class="p">)</span><span class="w">
  </span><span class="n">scale_number</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">info</span><span class="p">[</span><span class="n">direction</span><span class="p">]</span><span class="o">/</span><span class="m">500</span><span class="p">)</span><span class="w">
  </span><span class="n">image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_scale</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="n">paste0</span><span class="p">(</span><span class="n">info</span><span class="p">[</span><span class="s2">"width"</span><span class="p">]</span><span class="o">/</span><span class="n">scale_number</span><span class="p">,</span><span class="w">
                                             </span><span class="s2">"x"</span><span class="p">,</span><span class="w"> 
                                             </span><span class="n">info</span><span class="p">[</span><span class="s2">"height"</span><span class="p">]</span><span class="o">/</span><span class="n">scale_number</span><span class="p">))</span><span class="w">
  </span><span class="n">newinfo</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_info</span><span class="p">(</span><span class="n">image</span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># colours</span><span class="w">
  </span><span class="n">colours</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">try</span><span class="p">(</span><span class="n">rPlotter</span><span class="o">::</span><span class="n">extract_colours</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">num_col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">silent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># one pic at least was problematic </span><span class="w">
  </span><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">is</span><span class="p">(</span><span class="n">colours</span><span class="p">,</span><span class="w"> </span><span class="s2">"try-error"</span><span class="p">)){</span><span class="w">
    </span><span class="n">colour</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">colours</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w">
    
    </span><span class="n">image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_border</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="n">colour</span><span class="p">,</span><span class="w"> </span><span class="n">paste0</span><span class="p">((</span><span class="m">500</span><span class="o">-</span><span class="n">newinfo</span><span class="o">$</span><span class="n">width</span><span class="p">)</span><span class="o">/</span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="s2">"x"</span><span class="p">,</span><span class="w">
                                                        </span><span class="p">(</span><span class="m">500</span><span class="o">-</span><span class="n">newinfo</span><span class="o">$</span><span class="n">height</span><span class="p">)</span><span class="o">/</span><span class="m">2</span><span class="p">))</span><span class="w">
    </span><span class="n">info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_info</span><span class="p">(</span><span class="n">image</span><span class="p">)</span><span class="w">
    </span><span class="c1"># odd numbers out!</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="n">info</span><span class="o">$</span><span class="n">height</span><span class="o">/</span><span class="m">2</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nf">floor</span><span class="p">(</span><span class="n">info</span><span class="o">$</span><span class="n">height</span><span class="o">/</span><span class="m">2</span><span class="p">)){</span><span class="w">
      </span><span class="n">image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_crop</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="s2">"0x500+0"</span><span class="p">)</span><span class="w">
    </span><span class="p">}</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="n">info</span><span class="o">$</span><span class="n">width</span><span class="o">/</span><span class="m">2</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nf">floor</span><span class="p">(</span><span class="n">info</span><span class="o">$</span><span class="n">width</span><span class="o">/</span><span class="m">2</span><span class="p">)){</span><span class="w">
      </span><span class="n">image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_crop</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="s2">"500x0+0"</span><span class="p">)</span><span class="w">
    </span><span class="p">}</span><span class="w">
    </span><span class="n">magick</span><span class="o">::</span><span class="n">image_write</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w">
                        </span><span class="n">stringr</span><span class="o">::</span><span class="n">str_replace</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="s2">"nature"</span><span class="p">,</span><span class="w"> </span><span class="s2">"formatted_pics"</span><span class="p">))</span><span class="w">
    </span><span class="n">tibble</span><span class="o">::</span><span class="n">tibble</span><span class="p">(</span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">path</span><span class="p">,</span><span class="w">
                   </span><span class="n">colour</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">colour</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="k">else</span><span class="p">{</span><span class="w">
    </span><span class="kc">NULL</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  
  
</span><span class="p">}</span><span class="w">

</span><span class="n">pics_main_colours</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map_df</span><span class="p">(</span><span class="n">dir</span><span class="p">(</span><span class="s2">"nature"</span><span class="p">,</span><span class="w"> </span><span class="n">full.names</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w"> </span><span class="n">format_image</span><span class="p">)</span><span class="w">
</span><span class="n">readr</span><span class="o">::</span><span class="n">write_csv</span><span class="p">(</span><span class="n">pics_main_colours</span><span class="p">,</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"pics_main_colours.csv"</span><span class="p">)</span><span class="w">
</span>

And because I’m apparently a bad planner, I had to reduce pictures afterwards.

<span class="c1"># we need smaller images</span><span class="w">
</span><span class="n">reduce_image</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">path</span><span class="p">){</span><span class="w">
  </span><span class="n">magick</span><span class="o">::</span><span class="n">image_read</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">magick</span><span class="o">::</span><span class="n">image_scale</span><span class="p">(</span><span class="s2">"50x50!"</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">magick</span><span class="o">::</span><span class="n">image_write</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">purrr</span><span class="o">::</span><span class="n">walk</span><span class="p">(</span><span class="n">dir</span><span class="p">(</span><span class="s2">"formatted_pics"</span><span class="p">,</span><span class="w"> </span><span class="n">full.names</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
            </span><span class="n">reduce_image</span><span class="p">)</span><span class="w">
</span>

Preparing a function to order and paste pictures

This function has a collage part which you might recognize from my the faces of #rstats Twitter blog post, and a ordering pictures according to a variable part that’s new and uses a bit of tidy eval… Maybe I’ll really learn tidy eval this year! pics_info needs to be a data.frame with the path to pictures and well the variable one wants to use to order them.

<span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"rlang"</span><span class="p">)</span><span class="w">

</span><span class="n">make_column</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">files</span><span class="p">,</span><span class="w"> </span><span class="n">no_rows</span><span class="p">){</span><span class="w">
  </span><span class="n">magick</span><span class="o">::</span><span class="n">image_read</span><span class="p">(</span><span class="n">files</span><span class="p">[(</span><span class="n">i</span><span class="o">*</span><span class="n">no_rows</span><span class="m">+1</span><span class="p">)</span><span class="o">:</span><span class="p">((</span><span class="n">i</span><span class="m">+1</span><span class="p">)</span><span class="o">*</span><span class="n">no_rows</span><span class="p">)])</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">magick</span><span class="o">::</span><span class="n">image_append</span><span class="p">(</span><span class="n">stack</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">magick</span><span class="o">::</span><span class="n">image_write</span><span class="p">(</span><span class="n">paste0</span><span class="p">(</span><span class="s2">"cols/"</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="s2">".jpg"</span><span class="p">))</span><span class="w">
</span><span class="p">}</span><span class="w">


</span><span class="n">make_collage</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="n">no_rows</span><span class="p">,</span><span class="w"> </span><span class="n">no_cols</span><span class="p">,</span><span class="w"> </span><span class="n">ordering_col</span><span class="p">){</span><span class="w">
  </span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">arrange</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="o">!!!</span><span class="n">syms</span><span class="p">(</span><span class="n">ordering_col</span><span class="p">))</span><span class="w">
  </span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">pics_info</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">no_rows</span><span class="o">*</span><span class="n">no_cols</span><span class="p">),]</span><span class="w">
  </span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="n">column</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">no_cols</span><span class="p">,</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">no_rows</span><span class="p">))</span><span class="w">
  </span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">group_by</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="n">column</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">dplyr</span><span class="o">::</span><span class="n">arrange</span><span class="p">(</span><span class="o">!!!</span><span class="n">syms</span><span class="p">(</span><span class="n">ordering_col</span><span class="p">))</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">row</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">no_rows</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">dplyr</span><span class="o">::</span><span class="n">ungroup</span><span class="p">()</span><span class="w">
  
  </span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">arrange</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="n">column</span><span class="p">,</span><span class="w"> </span><span class="n">row</span><span class="p">)</span><span class="w">
  
  </span><span class="n">dir.create</span><span class="p">(</span><span class="s2">"cols"</span><span class="p">)</span><span class="w">
  </span><span class="n">purrr</span><span class="o">::</span><span class="n">walk</span><span class="p">(</span><span class="m">0</span><span class="o">:</span><span class="p">(</span><span class="n">no_cols</span><span class="m">-1</span><span class="p">),</span><span class="w"> </span><span class="n">make_column</span><span class="p">,</span><span class="w"> </span><span class="n">files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pics_info</span><span class="o">$</span><span class="n">path</span><span class="p">,</span><span class="w">
              </span><span class="n">no_rows</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">no_rows</span><span class="p">)</span><span class="w">
  
  </span><span class="n">banner</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">magick</span><span class="o">::</span><span class="n">image_read</span><span class="p">(</span><span class="n">dir</span><span class="p">(</span><span class="s2">"cols/"</span><span class="p">,</span><span class="w"> </span><span class="n">full.names</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">))</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">magick</span><span class="o">::</span><span class="n">image_append</span><span class="p">(</span><span class="n">stack</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w"> 
  
  </span><span class="n">unlink</span><span class="p">(</span><span class="s2">"cols"</span><span class="p">,</span><span class="w"> </span><span class="n">recursive</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
  
  </span><span class="nf">return</span><span class="p">(</span><span class="n">banner</span><span class="p">)</span><span class="w">
  
</span><span class="p">}</span><span class="w">

</span>

The function returns a magick object that one can then write to disk as a PNG for instance.

I first tested it using a random approach added to the data.frame created in the next section, and show the result here to give an idea of the variety of pictures. For many of them, however, the main colour that you can see in their border is greyish.

<span class="n">set.seed</span><span class="p">(</span><span class="m">42</span><span class="p">)</span><span class="w">
</span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="n">random</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">pics_info</span><span class="p">),</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">pics_info</span><span class="p">)))</span><span class="w">
</span><span class="n">make_collage</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="m">19</span><span class="p">,</span><span class="w"> </span><span class="m">59</span><span class="p">,</span><span class="w"> </span><span class="s2">"random"</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w"> 
  </span><span class="n">magick</span><span class="o">::</span><span class="n">image_write</span><span class="p">(</span><span class="s2">"data/2018-01-07-rainbowing-banner_random.png"</span><span class="p">)</span><span class="w">
</span>

Testing a first (bad) approach: using hue

Once I had the main colour as an hex code, I had no idea how to order the colours and thought a good idea would be to use hue, which is the main wave length in a colour. Most observed colours are a mix of wave lengths unless you’re using a laser for instance. To get hue from a colour identified by its hex code, one needs two functions: colorspace::hex2rgb and grDevices::rgb2hsv. The latter one outputs hue, saturation and value. Hue is the main wavelength, saturation the amount of that wavelength in the colour and value the amount of light in the colour. The smaller the saturation, the less representative the hue is of the main colour. Add to that the fact that the main colour can also be only a little representative of your original picture… Ordering by hue isn’t too perfect, but I tried that anyway.

<span class="c1"># now work on getting the hue and value for all pics</span><span class="w">
</span><span class="c1"># create a data.frame with path, hue, value </span><span class="w">
</span><span class="n">get_values</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">pics_main_colours</span><span class="p">){</span><span class="w">
  </span><span class="n">print</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="w">
  </span><span class="c1"># get main color</span><span class="w">
  </span><span class="n">colour</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">pics_main_colours</span><span class="o">$</span><span class="n">colour</span><span class="p">[</span><span class="n">pics_main_colours</span><span class="o">$</span><span class="n">path</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">stringr</span><span class="o">::</span><span class="n">str_replace</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w">
                                                                      </span><span class="s2">"formatted_pics"</span><span class="p">,</span><span class="w">
                                                                      </span><span class="s2">"nature"</span><span class="p">)]</span><span class="w">
  
  </span><span class="c1"># translate it</span><span class="w">
  </span><span class="n">rgb</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">colorspace</span><span class="o">::</span><span class="n">hex2RGB</span><span class="p">(</span><span class="n">colour</span><span class="p">)</span><span class="w">
  </span><span class="n">values</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">grDevices</span><span class="o">::</span><span class="n">rgb2hsv</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="n">rgb</span><span class="o">@</span><span class="n">coords</span><span class="p">))</span><span class="w">
  
  </span><span class="n">tibble</span><span class="o">::</span><span class="n">tibble</span><span class="p">(</span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">path</span><span class="p">,</span><span class="w">
                 </span><span class="n">hue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">values</span><span class="p">[</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">],</span><span class="w">
                 </span><span class="n">saturation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">values</span><span class="w"> </span><span class="p">[</span><span class="m">2</span><span class="p">,</span><span class="m">1</span><span class="p">],</span><span class="w">
                 </span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">values</span><span class="p">[</span><span class="m">3</span><span class="p">,</span><span class="m">1</span><span class="p">])</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># all values</span><span class="w">
</span><span class="n">pics_col</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map_df</span><span class="p">(</span><span class="n">dir</span><span class="p">(</span><span class="s2">"formatted_pics"</span><span class="p">,</span><span class="w"> </span><span class="n">full.names</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
                          </span><span class="n">get_values</span><span class="p">,</span><span class="w"> </span><span class="n">pics_main_colours</span><span class="p">)</span><span class="w">
                          



</span><span class="n">make_collage</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> </span><span class="m">19</span><span class="p">,</span><span class="w"> </span><span class="m">59</span><span class="p">,</span><span class="w"> </span><span class="s2">"hue"</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w"> 
  </span><span class="n">magick</span><span class="o">::</span><span class="n">image_write</span><span class="p">(</span><span class="s2">"banner_hue.png"</span><span class="p">)</span><span class="w">

</span>

So this is not too pretty. Blue and green pictures seem to cluster together but there are very dark pictures which we’d intuitively put aside.

So I thought a bit more and decided to first assign main colours to a reference colour and then order pictures based on this…

Choosing a better approach: RGB and distances

The first challenge was to choose reference colours which’d be a rainbow slices. I could have looked up RGB codes corresponding to ROYGBIV (red, orange, yellow, green, blue, indigo and violet.) but I had read about xkcd colors survey in this interesting post and therefore decided to use XKCD colors, available in R via the xkcdcolors package. I chose to use the 18 most common ones, and add black to that lot. It was no longer really a rainbow, I agree. The colors present in the pictures were ordered by hand by my husband, and I like his choices.

Then to assign each pic to a reference colour via its main colour, I calculated the Euclidian distance between that colour and all reference colours to find the closes reference colours, using the RGB values.

<span class="n">library</span><span class="p">(</span><span class="s2">"xkcdcolors"</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"magrittr"</span><span class="p">)</span><span class="w">
</span><span class="c1"># version of colorspace::hex2RGB returning a df</span><span class="w">
</span><span class="n">hex2rgb</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">hex</span><span class="p">){</span><span class="w">
  </span><span class="n">result</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">colorspace</span><span class="o">::</span><span class="n">hex2RGB</span><span class="p">(</span><span class="n">hex</span><span class="p">)</span><span class="o">@</span><span class="n">coords</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># https://stackoverflow.com/questions/45328221/unnest-one-column-list-to-many-columns-in-tidyr</span><span class="w">
</span><span class="n">colors</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">tibble</span><span class="o">::</span><span class="n">tibble</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">xcolors</span><span class="p">()[</span><span class="m">1</span><span class="o">:</span><span class="m">18</span><span class="p">],</span><span class="w"> </span><span class="s2">"black"</span><span class="p">),</span><span class="w">
                         </span><span class="n">hex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name2color</span><span class="p">(</span><span class="n">name</span><span class="p">),</span><span class="w">
                         </span><span class="n">rgb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map</span><span class="p">(</span><span class="n">hex</span><span class="p">,</span><span class="w"> </span><span class="n">hex2rgb</span><span class="p">))</span><span class="w"> </span><span class="o">%>%</span><span class="w"> 
  </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">rgb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span><span class="w"> </span><span class="n">tibble</span><span class="o">::</span><span class="n">as_tibble</span><span class="p">))</span><span class="w"> </span><span class="o">%>%</span><span class="w">
  </span><span class="n">tidyr</span><span class="o">::</span><span class="n">unnest</span><span class="p">()</span><span class="w">

</span><span class="c1"># for each colour I want the closest one.</span><span class="w">
</span><span class="n">find_closest_colour</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">hex</span><span class="p">,</span><span class="w"> </span><span class="n">colors</span><span class="p">){</span><span class="w">
  </span><span class="n">test</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">tibble</span><span class="o">::</span><span class="n">tibble</span><span class="p">(</span><span class="n">hex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hex</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w"> 
    </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">rgb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map</span><span class="p">(</span><span class="n">hex</span><span class="p">,</span><span class="w"> </span><span class="n">hex2rgb</span><span class="p">),</span><span class="w">
                  </span><span class="n">rgb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span><span class="w"> </span><span class="n">tibble</span><span class="o">::</span><span class="n">as_tibble</span><span class="p">))</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">tidyr</span><span class="o">::</span><span class="n">unnest</span><span class="p">()</span><span class="w"> 
  
  </span><span class="n">distance</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">stats</span><span class="o">::</span><span class="n">dist</span><span class="p">(</span><span class="n">rbind</span><span class="p">(</span><span class="n">test</span><span class="p">[,</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"R"</span><span class="p">,</span><span class="w"> </span><span class="s2">"G"</span><span class="p">,</span><span class="w"> </span><span class="s2">"B"</span><span class="p">)],</span><span class="w">
                                </span><span class="n">colors</span><span class="p">[,</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"R"</span><span class="p">,</span><span class="w"> </span><span class="s2">"G"</span><span class="p">,</span><span class="w"> </span><span class="s2">"B"</span><span class="p">)]))</span><span class="w">
  
  </span><span class="n">colors</span><span class="o">$</span><span class="n">name</span><span class="p">[</span><span class="n">which.min</span><span class="p">(</span><span class="n">as.matrix</span><span class="p">(</span><span class="n">distance</span><span class="p">)[,</span><span class="m">1</span><span class="p">][</span><span class="m">2</span><span class="o">:</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)])]</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">imgs_col</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">pics_main_colours</span><span class="p">,</span><span class="w">
                          </span><span class="n">xkcd_col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">purrr</span><span class="o">::</span><span class="n">map_chr</span><span class="p">(</span><span class="n">colour</span><span class="p">,</span><span class="w"> </span><span class="n">find_closest_colour</span><span class="p">,</span><span class="w">
                                                    </span><span class="n">colors</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">colors</span><span class="p">))</span><span class="w">


</span><span class="n">readr</span><span class="o">::</span><span class="n">write_csv</span><span class="p">(</span><span class="n">imgs_col</span><span class="p">,</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"imgs_xkcd_col.csv"</span><span class="p">)</span><span class="w">
</span>

Once I had this information about each pic, I could order the pictures, after having defined the order of the reference colours.

<span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">readr</span><span class="o">::</span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"imgs_xkcd_col.csv"</span><span class="p">)</span><span class="w">
</span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w"> 
                           </span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">stringr</span><span class="o">::</span><span class="n">str_replace</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="s2">"nature"</span><span class="p">,</span><span class="w"> </span><span class="s2">"formatted_pics"</span><span class="p">))</span><span class="w">

</span><span class="n">pics_info</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">dplyr</span><span class="o">::</span><span class="n">mutate</span><span class="p">(</span><span class="n">pics_info</span><span class="p">,</span><span class="w">
                           </span><span class="n">xkcd_col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">xkcd_col</span><span class="p">,</span><span class="w"> </span><span class="n">ordered</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
                                             </span><span class="n">levels</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"black"</span><span class="p">,</span><span class="s2">"brown"</span><span class="p">,</span><span class="s2">"red"</span><span class="p">,</span><span class="s2">"magenta"</span><span class="p">,</span><span class="s2">"pink"</span><span class="p">,</span><span class="w">
                                                        </span><span class="s2">"lime green"</span><span class="p">,</span><span class="s2">"green"</span><span class="p">,</span><span class="s2">"dark green"</span><span class="p">,</span><span class="s2">"teal"</span><span class="p">,</span><span class="w">
                                                        </span><span class="s2">"light blue"</span><span class="p">,</span><span class="s2">"sky blue"</span><span class="p">,</span><span class="s2">"blue"</span><span class="p">,</span><span class="s2">"purple"</span><span class="p">,</span><span class="s2">"grey"</span><span class="p">)))</span><span class="w">

</span>

This looks much better, but of course the initial set (and maybe the used extraction method as well) don’t provide for enough colours to make this extremely pretty. I’m not sure how useful this end product is, but hey I got to look at pretty landscapes full of colours from my grey rainy city, and learnt a lot along the way… Besides, maybe you will find a cool use case of some of the colour methods featured here and will tell me about it in the comments?

To leave a comment for the author, please follow the link and comment on their blog: Maëlle.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)