J'ai des centaines de petites images PNG en noir et blanc pur (300x300 pixels). Je veux les convertir en deux couleurs différentes dans R et les enregistrer à nouveau au format PNG. (Tout d'abord, je veux les inverser : tout noir en blanc et tout blanc en noir - mais plus tard j'aurai besoin d'autres couleurs, par exemple du noir au rouge et du blanc au vert, etc.) Cela semble très simple, mais quoi que j'essaye, je cours en problèmes.

Par exemple, la solution la plus simple semble utiliser as.raster en base R et le package png (au moins pour la lecture) :

img = readPNG(newfile) # read black (background) and white (figure) image
img <- as.raster(img) 
img_white = img
img_white[img_white == "#000000"] <- 'red' # temporarily convert back to red as placeholder
img_white[img_white == "#FFFFFF"] <- '#000000' # convert white to black
img_white[img_white == "red"] <- '#FFFFFF' # convert originally black to white

(Ici en passant, j'avais besoin d'un espace réservé car la couleur de l'objectif est la même que l'autre original - mais ce n'est pas la question.)

Cela fonctionne donc bien et je peux le tracer avec plot(img_white), mais incroyablement, je ne trouve aucun moyen d'enregistrer automatiquement l'image en tant que fichier. J'ai essayé par ex. writePNG, writeRaster, writeGDAL, mais ils donnent tous divers messages d'erreur en raison d'une mauvaise classe ou d'un mauvais format ou similaire. (J'ai également essayé diverses conversions sans succès.)

Entre autres, j'ai également essayé le package imager, qui enregistre bien l'image après l'avoir manipulée, mais je ne trouve pas le moyen de convertir une seule couleur spécifiée dans l'image entière.

Dans l'ensemble, je suis ouvert à toutes les solutions possibles tant que cela donne un code fonctionnel complet. Je me fiche du package que je dois utiliser, bien que si possible, je préfère un code aussi simple que possible et donc aussi peu de packages que possible.


SOLUTION:

Sur la base de la réponse d'Allan Cameron, j'ai écrit cette fonction :

change_cols = function(replace_black, replace_white, theimg) {
    r_b = col2rgb(replace_black) / 255
    r_w = col2rgb(replace_white) / 255
    theimg[theimg == 1] <- 2
    for (i in 1:3) {
        theimg[,,i][theimg[,,i] == 0] <- r_b[i]
    }
    for (i in 1:3) {
        theimg[,,i][theimg[,,i] == 2] <- r_w[i]
    }
    return(theimg)
}

C'est aussi simple que:

img = readPNG(newfile)
newimg = change_cols("#FF0000", "#00FF00", img)
writePNG(newimg, "fileout.png")

(Voir aussi la fonction d'Allan Cameron qui convertit l'objet raster.)

1
gaspar 22 janv. 2020 à 16:44

1 réponse

Meilleure réponse

Vous devez écrire le PNG sous forme de tableau numérique, comme il l'était lorsque vous l'avez chargé. Puisque vous n'avez que des images en noir et blanc, cela ne devrait pas poser de problème d'échanger manuellement le noir et le blanc (elles ont la valeur noir = 0, blanc = 1).

Il vous suffit de le convertir en raster pour le traçage :

library(png)
newfile = "~/face.png"
img = readPNG(newfile) # read black (background) and white (figure) image
img_white = 1-img

Maintenant

plot(raster::as.raster(img))

entrez la description de l'image ici Et

plot(raster::as.raster(img_white))

enter image description here

Ou si vous souhaitez inverser un seul canal (dans ce cas rouge) :

img[,,1] <- 1 - img[,,1]
plot(raster::as.raster(img))

enter image description here


ÉDITER

Après d'autres commentaires de l'OP, j'ai pensé qu'il était raisonnable de mener cette réponse à sa conclusion en écrivant une fonction qui prend un objet raster et l'enregistre en tant que fichier PNG :

save_raster_as_PNG <- function(raster_object, path) 
{
  if(class(raster_object) != "raster") stop("This is not a raster object.")
  dims        <- dim(raster_object)
  red         <- as.numeric(paste0("0x", substr(raster_object, 2 , 3)))/255
  dim(red)    <- rev(dims)
  green       <- as.numeric(paste0("0x", substr(raster_object, 4 , 5)))/255
  dim(green)  <- rev(dims)
  blue        <- as.numeric(paste0("0x", substr(raster_object, 6 , 7)))/255
  dim(blue)   <- rev(dims)
  result      <- numeric(3 * dims[1] * dims[2])
  dim(result) <- c(dims, 3)
  result[,,1] <- t(red)
  result[,,2] <- t(blue)
  result[,,3] <- t(green)

  tryCatch(png::writePNG(result, path), error = function(e) stop(e))
  cat("Raster successfully saved to", path.expand(path))
}

img <- raster::as.raster(img)
save_raster_as_PNG(img, "~/face3.png")
# Raster successfully saved to C:/Users/AllanCameron/SO/R/face3.png
2
Allan Cameron 22 janv. 2020 à 15:27