let's say I have
c = RGB{Normed{UInt8,8}}[
RGB{N0f8}(1.0,1.0,1.0) RGB{N0f8}(0.0,0.502,0.0) RGB{N0f8}(1.0,0.0,0.0);
RGB{N0f8}(1.0,0.0,0.0) RGB{N0f8}(1.0,1.0,1.0) RGB{N0f8}(0.0,0.0,0.0);
RGB{N0f8}(0.0,0.502,0.0) RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.502,0.0)]
How can I save this as a image in e.g. PNG or JPG format?
Note that I don't just need 9 pixels of those colors but like a bigger picture. Like:
You can use save function from Images.jlpackage (you should also install FileIO.jl and ImageMagick.jl for necessary transformations). To save it as a bigger picture you should manually resize the array to the required size with the help of repeat function
using Images
c = RGB{Normed{UInt8,8}}[
RGB{N0f8}(1.0,1.0,1.0) RGB{N0f8}(0.0,0.502,0.0) RGB{N0f8}(1.0,0.0,0.0);
RGB{N0f8}(1.0,0.0,0.0) RGB{N0f8}(1.0,1.0,1.0) RGB{N0f8}(0.0,0.0,0.0);
RGB{N0f8}(0.0,0.502,0.0) RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.502,0.0)]
c2 = repeat(c, inner = (50, 50))
save("/tmp/test.png", c2)
Here 50x50 in c2 definition is the size of a single cell in pixels.
Related
I have hundreds of small (300x300 pixel) pure black and white PNG images. I want to convert them to different two colors in R and save them again as PNG. (Firstly I want to actually invert them: all black to white and all white to black - but later I will need other colors, e.g. black to red and white to green, etc.) This seems real simple, but whatever I try I run into problems.
For example, the simplest solution seems using as.raster in base R and the png package (at least for reading):
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
(Here by the way I needed a placeholder because the goal color is the same as the other original - but that's beside the point.)
So this works nicely and I can plot it with plot(img_white), but incredibly I find no way of automatically saving the image as file. I tried e.g. writePNG, writeRaster, writeGDAL, but they all give various error messages due to wrong class or wrong format or similar. (I also tried various conversions without success.)
Among others I also tried the imager package, which nicely saves the image after manipulating it, but I cannot find the way to convert a single specified color in the entire image.
All in all, I'm open to any possible solutions as long as it gives a full working code. I don't much care whatever package I need to use, though if possible I'd prefer as simple code as possible and hence as few packages as possible.
SOLUTION:
Based on Allan Cameron's answer, I wrote this function:
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)
}
Then it's as simple as:
img = readPNG(newfile)
newimg = change_cols("#FF0000", "#00FF00", img)
writePNG(newimg, "fileout.png")
(See also Allan Cameron's function which converts the raster object.)
You need to write the PNG as a numeric array, just as it was when you loaded it. Since you only have black and white images, it shouldn't be a problem to manually swap black and white (they have value black = 0, white = 1).
You only need to convert it to a raster for plotting:
library(png)
newfile = "~/face.png"
img = readPNG(newfile) # read black (background) and white (figure) image
img_white = 1-img
Now
plot(raster::as.raster(img))
And
plot(raster::as.raster(img_white))
Or if you want to invert a single channel (in this case red):
img[,,1] <- 1 - img[,,1]
plot(raster::as.raster(img))
EDIT
After further comments from the OP, I thought it was reasonable to take this answer to its conclusion by writing a function that takes a raster object and saves it as a PNG file:
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
So I have a folder with some n images which I want to open and save with the readImage function. Right now a colleague had written something similar for opening and storing the name only of the images. I'd like to do the following:
setwd("~/ABC/One_Folder_Up")
img_src <- "FolderOfInterest"
image_list <- list.files(path=img_src, pattern = "^closed")
But with the actual .tif images named for example: closed100, closed101,....closed201
The above code works great for getting the names. But how can I get this type of pattern but instead open and save images? The output is a large matrix for each image.
So for n = 1 to n, I want to perform the following:
closed175 <- readImage("closed175.tif")
ave175 <- mean(closed175)
SD175 <- SD(closed175)
I'm assuming the image list shown in the first part could be used in the desired loop?
Then, after the images are saved as their own matricies, and all averages and SDs are calculated, I want to put the averages and SDs in a matrix like this:
imavelist <- c(ave175, ave176,......ave200)
Sorry, not an expert coder. Thank you!
edit: maybe lapply?
edit2: if I use this suggestion,
require(imager)
closed_images <- lapply(closed_im_list, readImage)
closed_im_matrix = do.call('cbind', lapply(closed_images, as.numeric))
Then I need a loop to save each element of the image stack matrix as its own individual image.
setwd("~/ABC/One_Folder_Up/FolderOfInterest/")
#for .tif format
image_list=list.files(path=getwd(), pattern = "*.tif")
# for other formats replace tif with appropriate format.
f=function(x){
y=readImage(x)
mve=mean(y)
sd=sd(y)
c(mve,sd)
}
results=data.frame(t(sapply(image_list,f)))
colnames(results)=c("average","sd")
the resul for 3 images:
> results
average sd
Untitled.tif 0.9761128 0.1451167
Untitled2.tif 0.9604224 0.1861798
Untitled3.tif 0.9782997 0.1457034
>
Assume we have a set of identical images imgs (see below). Note that the set length may vary in practice.
library(magick)
library(rsvg)
img <- image_read_svg("https://image.flaticon.com/icons/svg/132/132233.svg", width = 30)
imgs <- replicate(8, img)
The goal is to print a square image containing all the images within imgs (even though the set length may not be a square number):
I played around with image_append() and image_append(..., stack = TRUE) from the magick package without success [ref]. Ideally I would like a function (e.g. printMosaic(imgs)) that takes as input imgs and outputs the squared image displayed above. Maybe it would be easier to achieve with a different package?
That's a very nice question!
First, lets randomly select how many images we want and then automatically calculate how many rows/columns we will need.
# Number of images from 1 to 100
N <- sample(1:1e2, 1)
print(N)
[1] 84
# How many rows/columns we will need
X <- ceiling(sqrt(N))
print(X)
[1] 10
Create empty panel using multipanelfigure package with X rows and columns:
library(multipanelfigure)
figure <- multi_panel_figure(columns = X, rows = X)
# Iterate from 1 to N images and append them to figure
for(i in seq_len(N)) {
# "./R.png" is path to image I'm using
# With this package you don't need to worry about importing images
figure %<>% fill_panel("./R.png", label = "", scaling = "shrink")
}
I have a function that reads a multi-band image in as a raster brick object, iterates through the bands doing various calculations, and then writes the raster out as a new .tif. All of this works fine, but the file size of the new image file is roughly four times greater (I assume because the original image has 4 bands). I'm wondering if there's a parameter in the writeRaster() function that I'm unaware of, or if there's some other way I can ensure that the output image is basically the same file size as the input.
Original file size is 134 MB; output ranges from 471 to 530 MB or so, depending on format.
Simplified code:
library(rgdal)
library(raster)
path = "/Volumes/ENVI Standard Files/"
img = "qb_tile.img"
imageCorrection = function(path, img){
raster = brick(paste0(path, img))
raster = reclassify(raster, cbind(0, NA))
for(i in 1:nlayers(raster)){
raster[[i]] = raster[[i]] - minValue(raster[[i]])
}
writeRaster(raster, paste0(path,img,"_process.tif"), format = "GTiff", overwrite=TRUE)
}
You can set the default datatype for writing rasters with the rasterOptions() as follows:
rasterOptions(datatype="INT2U")
Or directly in the writeRaster call:
writeRaster(yourRas, "path/to/raster/", dataType="INT2U", options="COMPRESS=LZW")
Also notice the options argument where you can specify compression.
Usually when I export integer rasters from R, I make sure that I really have integers and not floats, since this can result in an empty raster. Try the following before exporting:
ras <- as.integer(ras)
Please note:
Also check for negative values in your raster. Try INT2S if you have values below zero.
Below is the first portion of the code I am using. The intention of this code is to, when given a file of images in .bmp format to correctly identity the letter shown in the image.
#Install required packages
Pkg.add("Images")
Pkg.add("DataFrames")
using Images
using DataFrames
#typeData could be either "train" or "test.
#labelsInfo should contain the IDs of each image to be read
#The images in the trainResized and testResized data files
#are 20x20 pixels, so imageSize is set to 400.
#path should be set to the location of the data files.
function read_data(typeData, labelsInfo, imageSize, path)
#Intialize x matrix
x = zeros(size(labelsInfo, 1), imageSize)
for (index, idImage) in enumerate(labelsInfoTrain["ID"])
#Read image file
nameFile = "$(path)/$(typeData)Resized/$(idImage).Bmp"
img = imread(nameFile)
#Convert img to float values
temp = float32sc(img)
#Convert color images to gray images
#by taking the average of the color scales.
if ndims(temp) == 3
temp = mean(temp.data, 1)
end
#Transform image matrix to a vector and store
#it in data matrix
x[index, :] = reshape(temp, 1, imageSize)
end
return x
end
imageSize = 400 # 20 x 20 pixels
#Set location of data files , folders
#Probably will need to set this path to which folder your files are in
path = "C:\\Users\\Aaron\\Downloads\\Math512Project"
#Read information about test data ( IDs )
labelsInfoTest = readtable("$(path)/sampleSubmissionJulia.csv")
#Read test matrixnformation about training data , IDs.
labelsInfoTrain = readtable("$(path)/trainLabels.csv")
#Read training matrix
xTrain = read_data("train", labelsInfoTrain, imageSize, path)
the error that I am facing is when the my program reaches the very last line of code above that reads:
xTrain = read_data("train", labelsInfoTrain, imageSize, path)
I receive an error saying: getindex has no method matching getindex(::DataFrame, ::ASCIIString in read_data at benchmarkJeff.jl:18
which refers to the line of code :
for (index, idImage) in enumerate(labelsInfoTrain["ID"])
Some research online has given me insight that the problem has to do with a conflict when using the DataFrames package and Image package. I was recommended to change the "ID" in my code to [:ID], but this does not solve the problem but rather causes another error. I was wondering if anyone new how to fix this problem or what exactly the problem is with my code. I get the same error when running the code in Julia command line 0.4.0. Look forward to hearing from you.