1. Fragestellung

In der Darstellung bestehen Bilder aus Pixel und der Farbe des Pixels. Die JPEG Norm (siehe z. B. (Hudson et al. 2017)) beschreibt z. B. ein Verfahren der Bildkompression.

Die Wahrnehmung von Farben unterscheidet sich aber, siehe z. B. (Paramei and Oakley 2014). In dieser kurzen Fallstudie soll untersucht werden, ob und wie die Anzahl der Farben durch eine einfache Clusteranalyse reduziert werden kann und wie das resultierenden Bild aussieht.

Eine andere Herangehensweise der Komplexitätsreduktion ist die Hauptkomponentenanalyse (siehe z. B. (Mudrova and Procházka 2005)). Auch dies wird vorgestellt.

2. Bilder und Pixel in R

Die Verschiedenfarbige Schwertlilie, lat.: iris versicolor, ist vermutlich eine der drei berühmtesten Blumen in der Statistik und Data-Science. An ihnen wurde im Jahre 1936 von Sir Ronald Aylmer Fisher die Grundlage der Linearen Diskriminanzanalyse als Beispiel eingeführt (Fisher 1936). Hier ein Foto von Armin Hauke:

Vorbereitungen

Pakete laden:

# Ggfs. Paket jpeg installieren
# install.packages("jpeg")
library(jpeg)
# Ggfs. Paket scales installieren
# install.packages("scales")
library(scales)
# Ggfs. Paket mosaic installieren
# install.packages("mosaic")
library(mosaic)

Über den Befehl readJPEG() aus dem Paket jpeg kann das Bild in R eingelesen werden:

img <- readJPEG("irisversi.jpg")

Darstellung des Bildes in R:

dimension <- dim(img)
dimension
## [1]  768 1024    3

Es handelt sich um ein 3-dimensionales array: \(768 \times 1024\) Pixel mit \(3\) Farben: Rot, Gelb, Blau.

In Datenmatrix überführen:

img_rgb <- data.frame(
  x = rep(1:dimension[2], each = dimension[1]), # Bildspalte
  y = rep(dimension[1]:1, dimension[2]), # Bildzeile
  R = as.vector(img[,,1]), # Rotanteil
  G = as.vector(img[,,2]), # Gelbanteil
  B = as.vector(img[,,3]) # Blauanteil
)

# Kontrolle
str(img_rgb)
## 'data.frame':    786432 obs. of  5 variables:
##  $ x: int  1 1 1 1 1 1 1 1 1 1 ...
##  $ y: int  768 767 766 765 764 763 762 761 760 759 ...
##  $ R: num  0.0824 0.0784 0.0706 0.0667 0.0627 ...
##  $ G: num  0.176 0.173 0.165 0.165 0.161 ...
##  $ B: num  0.0745 0.0706 0.0627 0.051 0.0471 ...

Anzahl unterschiedliche Farben:

n_color <- img_rgb %>%
  select(R,G,B) %>%
  unique() %>%
  nrow()

n_color
## [1] 191005

Für die \(786432=1024 \cdot 768\) Pixel (“Beobachtungen”) des Bildes werden 191005 verschiedene Farbtöne verwendet.

3. Clusteranalyse

k-Means Clusterung der Pixel auf Basis der Farben auf \(k=16\) Farbcluster:

set.seed(1896)
k_means <- kmeans(img_rgb[,c("R","G","B")], centers = 16, 
                  iter.max = 25, nstart = 10)

Farbdarstellung der resultierenden 16 Farben:

k_means$centers %>%
  rgb() %>%
  show_col()

Häufigkeitsverteilung der Clusterzentren (Farben):

gf_col(k_means$size ~ 1:16, fill = rgb(k_means$centers))

Es werden überwiegend Grün- und Lilatöne verwendet.

Erzeugung des Bildes, in dem jedem Pixel die Farbe des zugeordneten Zentrums zugewiesen wird:

# Datensatz
reduced.df <- k_means$centers[k_means$cluster,]
# Array
img_new <- array(NA, dimension)
for(i in 1:3) img_new[,,i] <- matrix(reduced.df[,i], nrow=dimension[1]) 
# Speichern
writeJPEG(img_new, "irisversi_reduced_cluster.jpg")

Das Bild, welches anstatt 191005 nur die 16 auf RGB Ebene gelusterten Farben hat sieht wie folgt aus: