magick 包: 在R中进行高级图片处理

最新的magick包是为在R中更现代、更简单高质量的进行图片处理而进行的一次努力。该包封装了目前最强大的开源图片库ImageMagick STL

ImageMagick库拥有大量的功能,目前的magick版本实现了其中一些很适用的功能模块,但是因为是第一次发布,现在文档还比较少。本文简单的介绍了其中一些最重要的概念来帮助了解magick

安装 magick

在windows或者OS-X系统中,最简单的方法就是直接通过CRAN进行安装。

install.packages("magick")

二进制的CRAN包只需少量的工作,就可以使绝大多数的重要特性得以实现。

magick_config() 函数查看你的ImageMagick版本支持哪些特性和图片格式。

library(magick)
## Linking to ImageMagick 6.9.9.39
## Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
## Disabled features: fftw, ghostscript, x11
str(magick::magick_config())
## List of 21
##  $ version           :Class 'numeric_version'  hidden list of 1
##   ..$ : int [1:4] 6 9 9 39
##  $ modules           : logi FALSE
##  $ cairo             : logi TRUE
##  $ fontconfig        : logi TRUE
##  $ freetype          : logi TRUE
##  $ fftw              : logi FALSE
##  $ ghostscript       : logi FALSE
##  $ jpeg              : logi TRUE
##  $ lcms              : logi TRUE
##  $ libopenjp2        : logi TRUE
##  $ lzma              : logi TRUE
##  $ pangocairo        : logi TRUE
##  $ pango             : logi TRUE
##  $ png               : logi TRUE
##  $ rsvg              : logi TRUE
##  $ tiff              : logi TRUE
##  $ webp              : logi TRUE
##  $ wmf               : logi FALSE
##  $ x11               : logi FALSE
##  $ xml               : logi TRUE
##  $ zero-configuration: logi TRUE

源代码编译安装

在Linux系统需要首先安装ImageMagick++库:Debian/Ubuntu系统中是libmagick++-dev:

sudo apt-get install libmagick++-dev

在Fedora或CentOS/RHEL系统中, 需要安装 ImageMagick-c++-devel:

sudo yum install ImageMagick-c++-devel

在OS-X系统中,为了从源码安装,需要利用homebrew安装 imagemagick@6 。

brew reinstall imagemagick@6 --with-fontconfig --with-librsvg
brew link --force imagemagick@6

homebrew中默认的imagemagick配置关闭了一部分特性。建议安装时至少加上--with-fontconfig 和 --with-librsvg选项来支持高质量的字体和svg渲染(CRAN上的OS-Xe二进制包已经默认配置好了)。

图片输入输出

magick包如此神奇的原因是它能自动转换和渲染所有常见的图片格式。ImageMagick支持许多格式,并自动检查类型。使用magick_config()函数查看你的ImageMagick版本支持哪些特性和图片格式。

读入和输出

图片可以通过image_read函数从文件路径、URL或图片原向量(raw vector)直接读取,image_info函数会输出一些图片的元数据(meta data),和imagemagick命令行中identify命令作用相同。

library(magick)
tiger <- image_read_svg('http://jeroen.github.io/images/tiger.svg', width = 400)
print(tiger)
##   format width height colorspace matte filesize density
## 1    PNG   400    400       sRGB  TRUE        0   72x72

 

我们可以使用image_write函数以任何格式将图片输出到本地硬盘,或当path = NULL(不指定路径)时输出到内存上。

# 渲染 svg 为 png 位图
image_write(tiger, path = "tiger.png", format = "png")

path是文件名,image_write执行成功则返回path,这让结果可以通过管道(pipe)传递给其他函数使用。

转换格式

magick在内存中按图片原格式进行存储,通过设置image_write函数中的format参数可以转换为其他格式。也可以在执行其他变换之前内在地进行格式转换。这当你不知道图片格式时尤其有用。

tiger_png <- image_convert(tiger, "png")
image_info(tiger_png)
##   format width height colorspace matte filesize density
## 1    PNG   400    400       sRGB  TRUE        0   72x72

注意,此时图片大小(size)为0,因为imagemagick是惰性加载原则,直到被使用之前都不会被渲染。

预览

拥有内置浏览器的集成编辑器(例如Rstudio)会在viewer中自动显示magick图片。也可以将结果巧妙的放在一个交互的图片编辑环境中。

rstudio

此外,Linux系统可以使用image_display函数在X11窗口中预览。image_browse会通过系统中默认应用来打开图片。

# 仅支持X11 
image_display(tiger)

# 系统依赖
image_browse(tiger)

另一种方法是把图片转换为栅格图,然后在R图形设备上绘制后进行显示。这种方法非常慢,仅仅在合并其他图形时比较有用。请查看后文#raster 。

转化

了解可用的转化的最佳途径是在RStudio中浏览?transformations帮助页面中的示例。 下面举几个例子来了解什么是可以实现的。

裁剪和编辑

一些转换函数中有geometry参数,该参数需要一个特殊的语法形式为AxB + C + D,其中每个元素都是可选的。 例子:

  • image_crop(image, "100x150+50")crop out width:100px and height:150px starting +50px from the left
  • image_scale(image, "200")resize proportionally to width: 200px
  • image_scale(image, "x200")resize proportionally to height: 200px
  • image_fill(image, "blue", "+100+200")flood fill with blue starting at the point at x:100, y:200
  • image_border(frink, "red", "20x10")adds a border of 20px left+right and 10px top+bottom

完整的语法设置请查看 Magick::Geometry 文档.

# 示例图片
frink <- image_read("https://jeroen.github.io/images/frink.png")
print(frink)
##   format width height colorspace matte filesize density
## 1    PNG   220    445       sRGB  TRUE    73494   72x72

 

# 左右两侧、上下两侧分别添加20像素、10像素边框
image_border(image_background(frink, "hotpink"), "#000080", "20x10")

# 去掉边框
image_trim(frink)

# Passport pica
image_crop(frink, "100x150+50")

 

# Resize
image_scale(frink, "300") # width: 300px

image_scale(frink, "x300") # height: 300px

# Rotate or mirror
image_rotate(frink, 45)

image_flip(frink)

image_flop(frink)

 

 

# Brightness, Saturation, Hue
image_modulate(frink, brightness = 80, saturation = 120, hue = 90)

# Paint the shirt orange
image_fill(frink, "orange", point = "+100+200", fuzz = 20)

使用image_fill我们可以从像素点point开始填充。fuzz参数允许以相同的颜色填充相邻的像素。 值必须在0到256^2之间,指距离之间之间的颜色是等同的。 在这里,我们给教授frink橙色衬衫世界杯。

滤镜和特效

ImageMagick还有一堆值得了解的标准特效。

# Add randomness
image_blur(frink, 10, 5)

image_noise(frink)

# Silly filters
image_charcoal(frink)

image_oilpaint(frink)

image_negate(frink)

核卷积(Kernel convolution)

image_convolve()函数在全图上实行kernel变换。和卷积是指在核矩阵中,图片每个像素的值根据相邻像素的加权和重新计算。例如,看下以下简单核变换:

kern <- matrix(0, ncol = 3, nrow = 3)
kern[1, 2] <- 0.25
kern[2, c(1, 3)] <- 0.25
kern[3, 2] <- 0.25
kern
##      [,1] [,2] [,3]
## [1,] 0.00 0.25 0.00
## [2,] 0.25 0.00 0.25
## [3,] 0.00 0.25 0.00

核变换将每个像素的值变为其水平和垂直相邻像素的平均值,这会在下面的右侧图像中产生轻微的模糊效果:

img <- image_resize(logo, "300x300")
img_blurred <- image_convolve(img, kern)
image_append(c(img, img_blurred))

或使用其他任何 标准核

img %>% image_convolve('Sobel') %>% image_negate()

img %>% image_convolve('DoG:0,0,2') %>% image_negate()

文本注释

最后,在图片上输出一些文本往往是十分有用的:

# Add some text
image_annotate(frink, "I like R!", size = 70, gravity = "southwest", color = "green")

 

 

# Customize text
image_annotate(frink, "CONFIDENTIAL", size = 30, color = "red", boxcolor = "pink",
  degrees = 60, location = "+50+100")

# Fonts may require ImageMagick has fontconfig
image_annotate(frink, "The quick brown fox", font = 'Times', size = 30)

大多数平台支持的字体包括 "sans"、 "mono"、 "serif"、 "Times"、 "Helvetica"、 "Trebuchet"、 "Georgia"、 "Palatino"、 "Comic Sans"

结合管道技术

每个图像变换函数返回原始图像的修改副本。 它不会影响原始图像。

frink <- image_read("https://jeroen.github.io/images/frink.png")
frink2 <- image_scale(frink, "100")
image_info(frink)
##   format width height colorspace matte filesize density
## 1    PNG   220    445       sRGB  TRUE    73494   72x72
image_info(frink2)
##   format width height colorspace matte filesize density
## 1    PNG   100    202       sRGB  TRUE        0   72x72

因此,要组合转换,需要链接它们:

test <- image_rotate(frink, 90)
test <- image_background(test, "blue", flatten = TRUE)
test <- image_border(test, "red", "10x10")
test <- image_annotate(test, "This is how we combine transformations", color = "white", size = 30)
print(test)
##   format width height colorspace matte filesize density
## 1    PNG   465    240       sRGB  TRUE        0   72x72

使用magrittr的管道语法会让这一过程更具可读性。

image_read("https://jeroen.github.io/images/frink.png") %>%
  image_rotate(270) %>%
  image_background("blue", flatten = TRUE) %>%
  image_border("red", "10x10") %>%
  image_annotate("The same thing with pipes", color = "white", size = 30)

图片向量

以上示例涉及单个图像。然而,magick中的所有函数都已经过向量化了,以支持使用图层,组合或动画。

标准基本方法[``[[c()length()用于处理图像向量,然后可以将其视为图层或帧。

# Download earth gif and make it a bit smaller for vignette
earth <- image_read("https://jeroen.github.io/images/earth.gif") %>%
  image_scale("200x") %>%
  image_quantize(128)

length(earth)
## [1] 44
earth

 

head(image_info(earth))
##   format width height colorspace matte filesize density
## 1    GIF   200    200        RGB FALSE        0   72x72
## 2    GIF   200    200        RGB FALSE        0   72x72
## 3    GIF   200    200        RGB FALSE        0   72x72
## 4    GIF   200    200        RGB FALSE        0   72x72
## 5    GIF   200    200        RGB FALSE        0   72x72
## 6    GIF   200    200        RGB FALSE        0   72x72
rev(earth) %>% 
  image_flip() %>% 
  image_annotate("meanwhile in Australia", size = 20, color = "white")

图层

我们可以像在Photoshop中一样堆叠彼此叠加的图层:

bigdata <- image_read('https://jeroen.github.io/images/bigdata.jpg')
frink <- image_read("https://jeroen.github.io/images/frink.png")
logo <- image_read("https://jeroen.github.io/images/Rlogo.png")
img <- c(bigdata, logo, frink)
img <- image_scale(img, "300x300")
image_info(img)
##   format width height colorspace matte filesize density
## 1   JPEG   300    225       sRGB FALSE        0   72x72
## 2    PNG   300    232       sRGB  TRUE        0   72x72
## 3    PNG   148    300       sRGB  TRUE        0   72x72

马赛克将图像添加在其他图片上,扩展输出画布来使整个图片更合适:

image_mosaic(img)

扁平地将图层组合成单个图片,该图具有第一个图像一样的大小:

image_flatten(img)

扁平合并和马赛克允许指定替代方案 composite operators:

image_flatten(img, 'Add')

image_flatten(img, 'Modulate')

image_flatten(img, 'Minus')

合并

附加(appending)意味着简单地将图片水平合并到一起:

image_append(image_scale(img, "x200"))

stack = TRUE 参数将所有图片垂直堆叠加到一起:

image_append(image_scale(img, "100"), stack = TRUE)

允许在特定位置组合两个图像:

bigdatafrink <- image_scale(image_rotate(image_background(frink, "none"), 300), "x200")
image_composite(image_scale(bigdata, "x400"), bigdatafrink, offset = "+180+100")

文档页

在读取PDF文档时,每个页面都成为向量的元素。请注意,PDF会在读取时呈现,因此需要直接指定密度(density)。

manual <- image_read_pdf('https://cloud.r-project.org/web/packages/magick/magick.pdf', density = 72)
image_info(manual)
##    format width height colorspace matte filesize density
## 1     PNG   612    792       sRGB  TRUE        0   72x72
## 2     PNG   612    792       sRGB  TRUE        0   72x72
## 3     PNG   612    792       sRGB  TRUE        0   72x72
## 4     PNG   612    792       sRGB  TRUE        0   72x72
## 5     PNG   612    792       sRGB  TRUE        0   72x72
## 6     PNG   612    792       sRGB  TRUE        0   72x72
## 7     PNG   612    792       sRGB  TRUE        0   72x72
## 8     PNG   612    792       sRGB  TRUE        0   72x72
## 9     PNG   612    792       sRGB  TRUE        0   72x72
## 10    PNG   612    792       sRGB  TRUE        0   72x72
## 11    PNG   612    792       sRGB  TRUE        0   72x72
## 12    PNG   612    792       sRGB  TRUE        0   72x72
## 13    PNG   612    792       sRGB  TRUE        0   72x72
## 14    PNG   612    792       sRGB  TRUE        0   72x72
## 15    PNG   612    792       sRGB  TRUE        0   72x72
## 16    PNG   612    792       sRGB  TRUE        0   72x72
## 17    PNG   612    792       sRGB  TRUE        0   72x72
## 18    PNG   612    792       sRGB  TRUE        0   72x72
## 19    PNG   612    792       sRGB  TRUE        0   72x72
## 20    PNG   612    792       sRGB  TRUE        0   72x72
## 21    PNG   612    792       sRGB  TRUE        0   72x72
## 22    PNG   612    792       sRGB  TRUE        0   72x72
## 23    PNG   612    792       sRGB  TRUE        0   72x72
## 24    PNG   612    792       sRGB  TRUE        0   72x72
## 25    PNG   612    792       sRGB  TRUE        0   72x72
## 26    PNG   612    792       sRGB  TRUE        0   72x72
## 27    PNG   612    792       sRGB  TRUE        0   72x72
## 28    PNG   612    792       sRGB  TRUE        0   72x72
## 29    PNG   612    792       sRGB  TRUE        0   72x72
manual[1]

动画

不仅可以图片向量元素视为图层,还可以称为动画中的帧。

image_animate(image_scale(img, "200x200"), fps = 1, dispose = "previous")

image_morph生成了一个n幅图片的序列,这些序列将一幅图变形到另一幅图上,生成动画:

newlogo <- image_scale(image_read("https://jeroen.github.io/images/Rlogo.png"), "x150")
oldlogo <- image_scale(image_read("https://developer.r-project.org/Logo/Rlogo-3.png"), "x150")
frames <- image_morph(c(oldlogo, newlogo), frames = 10)
image_animate(frames)

如果你读取已存在的GIF或视频文件,每帧都会转为图层:

# Foreground image
banana <- image_read("https://jeroen.github.io/images/banana.gif")
banana <- image_scale(banana, "150")
image_info(banana)
##   format width height colorspace matte filesize density
## 1    GIF   150    148       sRGB  TRUE        0   72x72
## 2    GIF   150    148       sRGB  TRUE        0   72x72
## 3    GIF   150    148       sRGB  TRUE        0   72x72
## 4    GIF   150    148       sRGB  TRUE        0   72x72
## 5    GIF   150    148       sRGB  TRUE        0   72x72
## 6    GIF   150    148       sRGB  TRUE        0   72x72
## 7    GIF   150    148       sRGB  TRUE        0   72x72
## 8    GIF   150    148       sRGB  TRUE        0   72x72

操作单独的图帧,然后放回到原来的动画中:

# Background image
background <- image_background(image_scale(logo, "200"), "white", flatten = TRUE)

# Combine and flatten frames
frames <- image_composite(background, banana, offset = "+70+30")

# Turn frames into animation
animation <- image_animate(frames, fps = 10)
print(animation)
##   format width height colorspace matte filesize density
## 1    gif   200    155       sRGB  TRUE        0   72x72
## 2    gif   200    155       sRGB  TRUE        0   72x72
## 3    gif   200    155       sRGB  TRUE        0   72x72
## 4    gif   200    155       sRGB  TRUE        0   72x72
## 5    gif   200    155       sRGB  TRUE        0   72x72
## 6    gif   200    155       sRGB  TRUE        0   72x72
## 7    gif   200    155       sRGB  TRUE        0   72x72
## 8    gif   200    155       sRGB  TRUE        0   72x72

动画可以保存为MPEG格式的GIF:

image_write(animation, "Rlogo-banana.gif")

绘图和图形设备

一个相对较新的附加功能是提供了一个本地图形设备,能生成magick图对象。这可以像常规设备一样用于绘图,或者打开一个图形设备,用像素坐标将图绘制到现有图片上。

图形设备

image_graph() 函数和png() 、 x11()函数一样,打开一个新的图形设备。它返回一个图对象到plot(s)函数输出。绘图设备的每一页都成为图对象的一帧。

# Produce image using graphics device
fig <- image_graph(width = 400, height = 400, res = 96)
ggplot2::qplot(mpg, wt, data = mtcars, colour = cyl)
dev.off()

可以轻松的使用常规图片操作进行图的后期处理。

# Combine
out <- image_composite(fig, frink, offset = "+70+30")
print(out)
##   format width height colorspace matte filesize density
## 1    PNG   400    400       sRGB  TRUE        0   72x72

绘图设备

使用图形设备的另一种方法是用像素坐标在现有图像上绘图。

# Or paint over an existing image
img <- image_draw(frink)
rect(20, 20, 200, 100, border = "red", lty = "dashed", lwd = 5)
abline(h = 300, col = 'blue', lwd = '10', lty = "dotted")
text(30, 250, "Hoiven-Glaven", family = "monospace", cex = 4, srt = 90)
palette(rainbow(11, end = 0.9))
symbols(rep(200, 11), seq(0, 400, 40), circles = runif(11, 5, 35),
  bg = 1:11, inches = FALSE, add = TRUE)
dev.off()
print(img)
##   format width height colorspace matte filesize density
## 1    PNG   220    445       sRGB  TRUE        0   72x72

默认情况下,image_draw()将所有边距设置为0,并使用图形坐标来匹配图像大小(以像素为单位)(宽度x高度),其中(0,0)是左上角。 请注意,这意味着y轴从顶部到底部增加,这与典型的图形坐标相反。 您可以通过将自定义xlimylimmar值传递给image_draw来覆盖所有这些默认设置。

动画图设备

图形设备支持多个帧,可以轻松创建动画图形。 下面的代码显示了如何使用magick图形设备来实现非常酷的[gganimate](https://github.com/dgrtwo/gganimate)包中的示例

library(gapminder)
library(ggplot2)
img <- image_graph(600, 340, res = 96)
datalist <- split(gapminder, gapminder$year)
out <- lapply(datalist, function(data){
  p <- ggplot(data, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
    scale_size("population", limits = range(gapminder$pop)) + geom_point() + ylim(20, 90) + 
    scale_x_log10(limits = range(gapminder$gdpPercap)) + ggtitle(data$year) + theme_classic()
  print(p)
})
dev.off()
animation <- image_animate(img, fps = 2)
print(animation)
##    format width height colorspace matte filesize density
## 1     gif   600    340       sRGB  TRUE        0   72x72
## 2     gif   600    340       sRGB  TRUE        0   72x72
## 3     gif   600    340       sRGB  TRUE        0   72x72
## 4     gif   600    340       sRGB  TRUE        0   72x72
## 5     gif   600    340       sRGB  TRUE        0   72x72
## 6     gif   600    340       sRGB  TRUE        0   72x72
## 7     gif   600    340       sRGB  TRUE        0   72x72
## 8     gif   600    340       sRGB  TRUE        0   72x72
## 9     gif   600    340       sRGB  TRUE        0   72x72
## 10    gif   600    340       sRGB  TRUE        0   72x72
## 11    gif   600    340       sRGB  TRUE        0   72x72
## 12    gif   600    340       sRGB  TRUE        0   72x72

将它写入文件你可以:

image_write(animation, "gapminder.gif")

栅格图

Magick图像也可以转换为栅格对象,以便与R的图形设备一起使用。 因此我们可以将它与其他图形工具结合起来。 但是要注意R的图形设备非常慢并且具有非常不同的坐标系统,这降低了图像的质量。

R自带的栅格函数

R 有一个 as.raster 函数,能将图片转换为字符串向量。Paul Murrell的这篇文章 Raster Images in R Graphics 做了一个很好的回顾。

plot(as.raster(frink))

 

 

# Print over another graphic
plot(cars)
rasterImage(frink, 21, 0, 25, 80)

 

grid 包

grid包可以更轻松地在图形设备上叠加栅格,而无需调整绘图的x / y坐标

library(ggplot2)
library(grid)
qplot(speed, dist, data = cars, geom = c("point", "smooth"))
grid.raster(frink)

raster 包

raster包有自己的位图类,对空间应用程序很有用。将图像转换为栅格的最简单方法是将其导出为tiff文件:

tiff_file <- tempfile()
image_write(frink, path = tiff_file, format = 'tiff')
r <- raster::brick(tiff_file)
raster::plotRGB(r)

 

 

也可以手动将位图数组转换为栅格对象,但这似乎会丢失一些元数据:

buf <- as.integer(frink[[1]])
rr <- raster::brick(buf)
raster::plotRGB(rr, asp = 1)

 

光栅包似乎也不支持透明度,而这在空间成像的背景下可能是有意义的。

OCR 文本提取

magick最新的功能是利用OCR技术从图片中提取文本。这需要tesseract包。

install.packages("tesseract")
img <- image_read("http://jeroen.github.io/images/testocr.png")
print(img)
##   format width height colorspace matte filesize density
## 1    PNG   640    480       sRGB  TRUE    23359   72x72

# Extract text
cat(image_ocr(img))
## This is a lot of 12 point text to test the
## cor code and see if it works on all types
## of file format.
## 
## The quick brown dog jumped over the
## lazy fox. The quick brown dog jumped
## over the lazy fox. The quick brown dog
## jumped over the lazy fox. The quick
## brown dog jumped over the lazy fox.

 

  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值