画像から素敵なカラーパレットをつくりたい


この記事は[R Shiny Advent Calendar 2017](https://qiita.com/advent-calendar/2017/shiny)の23日目の記事です！

# このアカウントがかっこいい

![スクリーンショット 2017-12-14 午後5.07.18.png](https://qiita-image-store.s3.amazonaws.com/0/143058/c03a2e8c-c66e-88e9-8f29-bedf39df00d8.png)

[designseeds](https://www.instagram.com/designseeds/)

このようなカラーチャートのことを[PANTONE](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%B3%E3%83%88%E3%83%B3)というらしいです。

# 画像からPANTONEをつくりたい

以下を実現するwebアプリケーションを作ってみたくなりました。

入力：画像ファイル
出力：素敵なPANTONE

画像からカラーコードを取得するパッケージはいくつかありますが、今回は[RImagePalette package](https://github.com/cran/RImagePalette)を使います！

画像から色を抽出する手法の中で、**median cut algorithm**をRで実装したパッケージです。

# イメージ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/143058/0b5c1aa7-90a2-705a-f340-44a1a19081a0.png)

# 詳細

## 画像ファイルアップロード

以前の記事[shinyをjQueryで拡張する](https://qiita.com/sasaki_K_sasaki/items/876b4481b6b02de1f5b2)でご紹介した方法を使って、画像の指定方法を３つ用意します。

イメージ(画質)
<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://t.co/IxZlWF8NUX">pic.twitter.com/IxZlWF8NUX</a></p>&mdash; sasaki_K_sasaki (@sasaki_K_sasaki) <a href="https://twitter.com/sasaki_K_sasaki/status/944371808618266624?ref_src=twsrc%5Etfw">2017年12月23日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### １．「By upload」
一般的なファイルアップロード機能で、クリックするとフォルダが開いてファイルを選ぶ形式
![image.png](https://qiita-image-store.s3.amazonaws.com/0/143058/38cda209-e823-2639-ed70-98d530138827.png)

### ２．「By URL」
画像があるページのURLを入力して、その画面をキャプチャする形式
![image.png](https://qiita-image-store.s3.amazonaws.com/0/143058/6e1d3745-baf6-e6ef-4512-79099c46d5ba.png)

### ３．「D&D」(ドラッグ&ドロップ)
ドラッグ&ドロップで画像をアップロードする形式
![スクリーンショット 2017-12-23 午前10.04.45（2）.png](https://qiita-image-store.s3.amazonaws.com/0/143058/5e64a7f2-a994-8f66-8379-32306037858c.png)

それぞれ見た目を整えるために、元の`material_file_input()`や`fileInput()`関数の一部を書き換えています。
書き換えた関数を`global.R`の中で定義すれば、元のパッケージに縛られないinputが作れるようになります！

例)`fileInput()`

```fileInput_before.R
fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
                      width = NULL, buttonLabel = "Browse...", placeholder = "No file selected") {
  
  restoredValue <- restoreInput(id = inputId, default = NULL)
  
  # Catch potential edge case - ensure that it's either NULL or a data frame.
  if (!is.null(restoredValue) && !is.data.frame(restoredValue)) {
    warning("Restored value for ", inputId, " has incorrect format.")
    restoredValue <- NULL
  }
  
  if (!is.null(restoredValue)) {
    restoredValue <- toJSON(restoredValue, strict_atomic = FALSE)
  }
  
  inputTag <- tags$input(
    id = inputId,
    name = inputId,
    type = "file",
    style = "display: none;",
    `data-restore` = restoredValue
  )
  
  if (multiple)
    inputTag$attribs$multiple <- "multiple"
  if (length(accept) > 0)
    inputTag$attribs$accept <- paste(accept, collapse=',')
  
  
  div(class = "form-group shiny-input-container",
      style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
      label %AND% tags$label(label),
      
      div(class = "input-group",
          tags$label(class = "input-group-btn",
                     span(class = "btn btn-default btn-file",
                          buttonLabel,
                          inputTag
                     )
          ),
          tags$input(type = "text", class = "form-control",
                     placeholder = placeholder, readonly = "readonly"
          )
      ),
      
      tags$div(
        id=paste(inputId, "_progress", sep=""),
        class="progress progress-striped active shiny-file-input-progress",
        tags$div(class="progress-bar")
      )
  )
}

```
↓↓↓ 一部変更

```fileInput_Changed.R
fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
                      width = NULL, buttonLabel = "Browse...", placeholder = "No file selected") {
  
  restoredValue <- restoreInput(id = inputId, default = NULL)
  
  # Catch potential edge case - ensure that it's either NULL or a data frame.
  if (!is.null(restoredValue) && !is.data.frame(restoredValue)) {
    warning("Restored value for ", inputId, " has incorrect format.")
    restoredValue <- NULL
  }
  
  if (!is.null(restoredValue)) {
    restoredValue <- toJSON(restoredValue, strict_atomic = FALSE)
  }
  
  inputTag <- tags$input(
    id = inputId,
    name = inputId,
    type = "file",
    style = "display: none;",
    `data-restore` = restoredValue
  )
  
  if (multiple)
    inputTag$attribs$multiple <- "multiple"
  if (length(accept) > 0)
    inputTag$attribs$accept <- paste(accept, collapse=',')
  
  
  div(class = "form-group shiny-input-container",
      style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
      
      div(class = "input-group",
          tags$label(class = "input-group-btn",
                     inputTag
          ),
          tags$span(class = "form-placeholder",  placeholder) ,
          tags$input(type = "text", class = "form-control",
                     readonly = "readonly"
          )
      )
  )
}

```

## カラーコード抽出
**RImagePalette**の、`image_palette()`を使います。以下、一部抜粋

```server.R
 yout_image <- readJPEG(input_file_path)
 palette <- image_palette(your_image, n = n_row, choice = choice)

```
- `your_image`：アップロードされた画像ファイル
- n ：抽出する色の数
- choice：色抽出に用いる要約統計量

## カラーコード可視化
**ggplot2**のbar chartを使ってPANTONEを作ります。以下、一部抜粋

```server.R

df <- data.frame(X = seq( 1 : n_row ),
                       Y = rep( 10, n_row) )
      
pantone <- ggplot(df,
                  aes(x = X, 
                      y = Y, 
                      fill = factor(X))
                      ) + 
            geom_bar(stat = "identity", width = 0.8) +
            coord_flip() +
            scale_fill_manual(values = palette) +
            theme_void() + 
            theme(legend.position="none")

```

bar以外を全て排除して、シンプルに作図しています。

イメージ
![palette_sample.png](https://qiita-image-store.s3.amazonaws.com/0/143058/72856e3f-1c5c-4252-1992-ac064c9302d0.png)

#　結果

イメージ(画質)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://t.co/N8Br1jxuk1">pic.twitter.com/N8Br1jxuk1</a></p>&mdash; sasaki_K_sasaki (@sasaki_K_sasaki) <a href="https://twitter.com/sasaki_K_sasaki/status/944581128941219840?ref_src=twsrc%5Etfw">2017年12月23日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


なんとなくはできたけど、そこまで素敵にはできませんでした！！
ファイルのアップロードにこだわりすぎて、色の抽出精度などに手が回っていません‥

コードは[github](https://github.com/sasakiK/ColorPaletteMaker)にあります。
demoは以下のコードで実行できると思います。よろしければ試してみてください！


```console.R
library(shiny)

runGitHub(repo = "ColorPaletteMaker", username = "sasakiK" )

```

[Np_Ur_](http://www.randpy.tokyo/about)さんにバトンをもどします！
明日明後日よろしくお願いいたします！



# 参考

[メディアンカット法による画像の減色](https://www.spicysoft.com/blog/spicy_tech/001253.html)
[Package ‘RImagePalette’](https://cran.r-project.org/web/packages/RImagePalette/RImagePalette.pdf)
[Rで解析：画像ファイルからカラーパレットを作成！「RImagePalette」パッケージ](https://www.karada-good.net/analyticsr/r-346)