3 重要な処理

3.1 線形補間デモザイク

3.1.1 簡易デモザイク処理の問題点

簡易デモザイク処理を使ったRAW現像

raw_array <- raw$raw_image
white_level <- 1024

wb_raw <- raw_array %>%
  black_level_correction(raw$black_level_per_channel, raw$raw_pattern) %>%
  white_balance(raw$camera_whitebalance, raw$raw_colors)
dms_img <- wb_raw %>%
  simple_demosaic %>%
  `/`(white_level)
gmm_img <- gamma_correction(dms_img, 2.2)

表示

gmm_img %>% ta %>% as.cimg %>% plot

現像後のサイズ

dim(gmm_img) %>% head(2)
## [1] 1232 1640

RAWデータのサイズ

dim(raw_array)
## [1] 2464 3280

JPEG画像の読み込み

jpg_img <- imager::load.image(raw_file)

拡大して比較

x1 <- 836
y1 <- 741
dx1 <- 100
dy1 <- 100

par(mfrow = c(1, 2))
gmm_img %>% ta %>% as.cimg %>%
  imsub(x %inr% c(x1, x1 + dx1), y %inr% c(y1, y1 + dy1)) %>%
  plot(interpolate = FALSE, main = "簡易デモザイク結果")
jpg_img %>%
  imsub(x %inr% (c(x1, x1 + dx1) * 2), y %inr% (c(y1, y1 + dy1) * 2)) %>%
  plot(interpolate = FALSE, main = "JPEG画像")

3.1.2 線形補完法

線形補間デモザイク

demosaic <- function(raw_array, raw_colors, pattern) {
  dms_img <- array(0, c(dim(raw_array), 1, 3))

  g <- raw_array
  g[raw_colors %in% c(0, 2)] <- 0
  g_filter <- array(c(0, 1, 0,
                      1, 4, 1,
                      0, 1, 0) / 4,
                    c(3, 3, 1, 1))
  G(dms_img) <- convolve(as.cimg(g), g_filter)

  r <- raw_array
  r[raw_colors != 0] <- 0
  r_filter <- array(c(1/4, 1/2, 1/4,
                      1/2,   1, 1/2,
                      1/4, 1/2, 1/4),
                    c(3, 3, 1, 1))
  R(dms_img) <- convolve(as.cimg(r), r_filter)

  b <- raw_array
  b[raw_colors != 2] <- 0
  # 青のフィルターは赤と共通
  B(dms_img) <- convolve(as.cimg(b), r_filter)

  dms_img
}

デモザイク処理

dms_full_img <- wb_raw %>%
  demosaic(raw$raw_colors, raw$raw_pattern)

サイズを確認

dms_full_img %>% dim %>% head(2)
## [1] 2464 3280

ガンマ補正処理

gmm_full_img <- gamma_correction(dms_full_img / white_level, 2.2)

比較

par(mfrow = c(1, 2))
gmm_img %>% ta %>% as.cimg %>%
  imsub(x %inr% c(x1, x1 + dx1), y %inr% c(y1, y1 + dy1)) %>%
  plot(interpolate = FALSE, main = "簡易デモザイク")
gmm_full_img %>% ta %>% as.cimg %>%
  imsub(x %inr% (c(x1, x1 + dx1) * 2), y %inr% (c(y1, y1 + dy1) * 2)) %>%
  plot(interpolate = FALSE, main = "線形補間デモザイク")

3.2 欠陥画素補正

TODO

3.3 カラーマトリクス補正

カラーマトリクス (CCM: Color Correction Matrix)

color_matrix <- matrix(c(6022, -2314, 394,
                         -936, 4728, 310,
                         300, -4324, 8126),
                       3, 3, byrow = TRUE) / 4096

カラーマトリクス補正処理

color_correction_matrix <- function(rgb_array, color_matrix) {
  ccm_img <- array(0, dim(rgb_array))
  for (col in 1:3) {
    ccm_img[,, 1, col] <-
      color_matrix[col, 1] * R(rgb_array) +
      color_matrix[col, 2] * G(rgb_array) +
      color_matrix[col, 3] * B(rgb_array)
  }
  ccm_img
}

比較

par(mfrow = c(1, 2))
gmm_full_img %>% ta %>% as.cimg %>%
  plot(main = "CCM補正なし")
dms_full_img %>%
  color_correction_matrix(color_matrix) %>%
  `/`(white_level) %>%
  gamma_correction(2.2) %>%
  ta %>% as.cimg %>% plot(main = "CCM補正あり")

3.4 シェーディング補正

3.4.1 レンズシェーディングの確認

RAW画像の読み込み

raw_file <- "flat.jpg"
raw <- rawpy$imread(raw_file)
raw_array <- raw$raw_image

RAW現像処理

blc_raw <- raw_array %>%
  black_level_correction(raw$black_level_per_channel, raw$raw_pattern)
original_img <- blc_raw %>%
  white_balance(raw$camera_whitebalance, raw$raw_colors) %>%
  demosaic(raw$raw_colors, raw$raw_pattern) %>%
  color_correction_matrix(color_matrix) %>%
  `/`(white_level) %>%
  gamma_correction(2.2)

画像表示

original_img %>% ta %>% as.cimg %>% plot

明るさの横方向の変化

library(tidyverse)

w <- ncol(raw_array)
h <- nrow(raw_array)
center_y <- h / 2
center_x <- w / 2
y <- center_y - 16

horizontal_shading_profile <- function(img, w, y) {
  seq(1, w - 32, 32) %>%
    map_dfr(function(x) list(r = mean(img[y:(y + 32), x:(x + 32), 1, 1]),
                             g = mean(img[y:(y + 32), x:(x + 32), 1, 2]),
                             b = mean(img[y:(y + 32), x:(x + 32), 1, 3]))) %>%
    map_dfr(~ . / max(.)) %>%
    mutate(pos = 1:n())
}
shading_profile <- horizontal_shading_profile(original_img, w, y)

ggplot(gather(shading_profile, "color", "value", -pos), aes(x = pos, y = value)) +
  geom_line(aes(color = color)) +
  ylim(0, 1) +
  scale_color_manual(values = c(r = "red", g = "green", b = "blue"))

3.4.2 レンズシェーディングのモデル化

value_df <- map_dfr(seq(1, h - 32, 32), function(y) {
  map_dfr(seq(1, w - 32, 32), function(x) {
    xx <- x + 16
    yy <- y + 16
    list(
      xx = xx,
      yy = yy,
      radial = (yy - center_y) * (yy - center_y) + (xx - center_x) * (xx - center_x),
      b = mean(blc_raw[seq(y, y + 32, 2), seq(x, x + 32, 2)]),
      g1 = mean(blc_raw[seq(y, y + 32, 2), seq(x + 1, x + 32, 2)]),
      g2 = mean(blc_raw[seq(y + 1, y + 32, 2), seq(x, x + 32, 2)]),
      r = mean(blc_raw[seq(y + 1, y + 32, 2), seq(x + 1, x + 32, 2)]))
  })
})

最大値でノーマライズしてグラフにして確認

norm_value_df <- value_df %>%
  transmute(radial,
            b = b / max(b),
            g1 = g1 / max(g1),
            g2 = g2 / max(g2),
            r = r / max(r))

colors <- c(r = "red", g1 = "green", g2 = "green", b = "blue")

ggplot(gather(norm_value_df, "color", "value", -radial), aes(x = radial, y = value)) +
  geom_point(aes(color = color)) +
  ylim(0, 1) +
  scale_color_manual(values = colors)

逆数のグラフ

inv_norm_value_df <- norm_value_df %>%
  select(-radial) %>%
  map_dfc(~ 1 / .) %>%
  add_column(radial = norm_value_df$radial, .before = TRUE)

ggplot(gather(inv_norm_value_df, "color", "value", -radial), aes(x = radial, y = value)) +
  geom_point(aes(color = color)) +
  scale_color_manual(values = colors)

1次式で近似

models <- colnames(inv_norm_value_df)[-1] %>%
  paste("~ radial") %>%
  map(~ lm(as.formula(.), inv_norm_value_df))

model_df <- models %>%
  map_dfr(~ as.list(.$coefficients)) %>%
  transmute(color = colnames(inv_norm_value_df)[-1], intercept = `(Intercept)`, slope = radial)

値を確認

model_df
## # A tibble: 4 x 3
##   color intercept       slope
##   <chr>     <dbl>       <dbl>
## 1 b         0.939 0.000000655
## 2 g1        0.962 0.000000653
## 3 g2        0.964 0.000000648
## 4 r         0.909 0.00000100

プロット

ggplot(model_df) +
  geom_abline(aes(intercept = intercept, slope = slope, color = color)) +
  xlim(0, 4e6) +
  ylim(0, 6) +
  scale_color_manual(values = colors)

3.4.3 レンズシェーディング補正

レンズシェーディング補正前 (ブラックレベル補正のみ)

blc_raw %>% t %>% as.cimg %>% plot

各画素に掛け合わせるゲインを計算

gain_map <- array(0, dim(raw_array))
for (y in seq(1, h, 2)) {
  for (x in seq(1, w, 2)) {
    r2 <- (y - center_y) ^ 2 + (x - center_x) ^ 2
    gain <- model_df$intercept + model_df$slope * r2
    gain_map[y, x] <- gain[1]
    gain_map[y, x + 1] <- gain[2]
    gain_map[y + 1, x] <- gain[3]
    gain_map[y + 1, x + 1] <- gain[4]
  }
}

ゲインをブラックレベル補正した画像に掛け合わせる

lsc_raw <- blc_raw * gain_map
lsc_raw %>% normalize %>% t %>% as.cimg %>% plot

レンズシェーディング補正後のフルカラー画像

shading_img <- lsc_raw %>%
  white_balance(raw$camera_whitebalance, raw$raw_colors) %>%
  demosaic(raw$raw_colors, raw$raw_pattern) %>%
  color_correction_matrix(color_matrix) %>%
  `/`(white_level) %>%
  gamma_correction(2.2)
shading_img %>% ta %>% as.cimg %>% plot

残っているシェーディング量を測定

shading_after <- horizontal_shading_profile(shading_img, w, center_y - 16)

ggplot(gather(shading_after, "color", "value", -pos), aes(x = pos, y = value)) +
  geom_line(aes(color = color)) +
  ylim(0, 1) +
  scale_color_manual(values = c(r = "red", g = "green", b = "blue"))

3.4.4 通常画像への適用

raw_file <- "chart.jpg"
raw <- rawpy$imread(raw_file)
raw_array <- raw$raw_image

blc_raw <- raw_array %>%
  black_level_correction(raw$black_level_per_channel, raw$raw_pattern)

レンズシェーディング補正なし

no_shading_img <- blc_raw %>%
  white_balance(raw$camera_whitebalance, raw$raw_colors) %>%
  demosaic(raw$raw_colors, raw$raw_pattern) %>%
  color_correction_matrix(color_matrix) %>%
  `/`(white_level) %>%
  gamma_correction(2.2)

レンズシェーディング補正あり

shading_img <- blc_raw %>%
  `*`(gain_map) %>%
  white_balance(raw$camera_whitebalance, raw$raw_colors) %>%
  demosaic(raw$raw_colors, raw$raw_pattern) %>%
  color_correction_matrix(color_matrix) %>%
  `/`(white_level) %>%
  gamma_correction(2.2)

比較

par(mfrow = c(1, 2))
no_shading_img %>% ta %>% as.cimg %>% plot(main = "レンズシェーディング補正なし")
shading_img %>% ta %>% as.cimg %>% plot(main = "レンズシェーディング補正あり")

良さそうなので補正処理を関数にする

lens_shading_correction <- function(raw_array, coef) {
  gain_map <- array(0, dim(raw_array))
  h <- nrow(raw_array)
  w <- ncol(raw_array)
  center_y <- h / 2
  center_x <- w / 2

  x <- 1:w - center_x
  y <- 1:h - center_y
  r2 <- matrix(y, h, w, byrow = FALSE) ^ 2 + matrix(x, h, w, byrow = TRUE) ^ 2

  gain_map[c(T, F), c(T, F)] <- r2[c(T, F), c(T, F)] * coef[1,]$slope + coef[1,]$intercept
  gain_map[c(T, F), c(F, T)] <- r2[c(T, F), c(F, T)] * coef[2,]$slope + coef[2,]$intercept
  gain_map[c(F, T), c(T, F)] <- r2[c(F, T), c(T, F)] * coef[3,]$slope + coef[3,]$intercept
  gain_map[c(F, T), c(F, T)] <- r2[c(F, T), c(F, T)] * coef[4,]$slope + coef[4,]$intercept

  raw_array * gain_map
}

確認

coef <- model_df %>% select(-color)
shading_img2 <- blc_raw %>%
  lens_shading_correction(coef) %>%
  white_balance(raw$camera_whitebalance, raw$raw_colors) %>%
  demosaic(raw$raw_colors, raw$raw_pattern) %>%
  color_correction_matrix(color_matrix) %>%
  `/`(white_level) %>%
  gamma_correction(2.2)

shading_img2 %>% ta %>% as.cimg %>% plot