购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第三节
magick包中的图片处理

在完成可视化任务的过程中,我们有时除了需要把数据映射到图表中,还需要在图表中添加图片,因此我们在此介绍一些与图片处理有关的操作。当然,读者也可以在读到本书与添加图片相关的内容时,再回过头来学习本部分。

R中涉及图片处理的包有magick、imager、imager Extra等,此处只介绍使用起来较为方便的magick包。

一、读入图片

# install.packages(c("magick", "imager"))

library(magick)

library(imager)

library(jpeg)

## 用image_read读取图片。如果图片为pdf文件,可用image_read_pdf函数读取。在jpg和png这两种格式中,我们最好选择png格式,因为它支持图片的透明度

image=image_read("read.jpg") # 课件中的图片

class(image) # "magick-image"

要注意的是,不同的读取图片函数得到的对象的类型不同。magick::image_read生成magick-image对象;imager::load.image得到的是cimage对象,jpeg::read JPEG和png::read PNG得到的是array或native Raster对象。

image # 显示图片及图片信息

# format width height…

# 1 JPEG 872 595…

图片尺寸是指图片的宽和高。例子中图片的宽和高是872和595,或者说,它的尺寸是“872×595”,这意味着图片在水平方向上每行有872个像素,在垂直方向上每列有595个像素;如果给每个像素一个序号的话,那么图片的左上角就是第一个像素,右下角就是最后一个像素(换句话说,左上角是图片像素位置的原点)。

info=image_info(image) # 用image_info函数也可以提取图片信息

class(info) # "tbl_df" "tbl" "data.frame"

as.numeric(info[1, 2: 3]) # 提取宽和高

现在我们再来看看电脑是如何储存图片的。

x1=load.image("read.jpg") # imager包中的函数

x1

# Image. Width: 872 pix Height: 595 pix Depth: 1 Colour channels: 3

计算机用四个维度来储存图片:除了宽和高之外,还有一个深度(Depth),它仅在视频中代表图片出现的时间顺序;颜色通道(Colour channels)是指颜色值,每一个像素都用其红/绿/蓝色的数值来表示。

现在我们再用数组来存储同一张图片。

x2=read JPEG("read.jpg") # jpeg包中的函数

dim(x2)

# [1] 595 872 3

这个数组包含3层,每一层都相当于一个有595行(相当于图片的高)和872列(相当于图片的宽)的矩阵。而数组的3个层分别对应着像素的红/绿/蓝值;如果图片包含透明度信息,那么这个数组就会有4层。

R(x1)[101: 105, 101: 105] # 用imager中的R函数、G函数、B函数可以提取红/绿/蓝值

t(x2[, , 1])[101: 105, 101: 105]

# 以上两种方法都用来提取红值的特定部分,能够得到相同的结果

下边我们继续对变量类型进行转化。

image_ra=as.raster(image) # 转化为raster对象

class(image_ra) # "raster"

image_ma=as.matrix(image_ra) # 把raster对象转化为矩阵对象

dim(image_ra) # 595 872

dim(image_ma) # 595 872

image_ma[51: 53, 101: 102] # 截取图片的一部分

# [, 1] [, 2]

# [1, ] "#f1e7b4ff" "#f2e6b2ff"

# [2, ] "#f4e8b4ff" "#f4e8b4ff"

# [3, ] "#f8edb7ff" "#f8edb7ff"

可见,raster对象可以被看成一个矩阵。一张图片原本用包含三个通道的数组来表示,而现在为什么能够用仅有两个维度的数值来表示呢?这是因为在这个矩阵中,每一个像素的红/蓝/绿值(可能还要加上透明度值)都已被合并成一个十六进制字符,因而只占用一个单元格。这个raster对象或矩阵对象的第一行第一列的单元格对应着图片的左上角。

## 用image_convert实现不同格式图片的互相转化

image_png=image_convert(image, format="png", matte=TRUE) # 转为支持透明度的png图片时,务必设置matte=TRUE

## 用image_write保存图片,注意文件名的后缀要跟图片本身的格式相符

# image_write(image_png, "new.png")

二、裁剪、翻转、伸缩

image=image_read("read.jpg")

## 裁剪图片边缘

## 当图片有多余的边缘(特别是白边)时,这个函数会非常有用。但需要注意的是,一方面,有时函数会裁掉过多的部分,在这种情况下,我们最好还是自己手动裁剪;另一方面,有时我们又会觉得裁剪得还不够多,此时可调大fuzz参数(0至100的数值,默认为0)

image=image_trim(image, fuzz=8)

image_info(image) # 裁剪边缘后图片变小

## 添加边缘

image_border(image, color="cyan", geometry="20x40") # geometry用来指定边缘的宽度

## 图片旋转

image_rotate(image, degrees=-45) # 旋转。第二个参数为角度

image_flip(image) # 上下翻转

image_flop(image) # 左右翻转

## 图片截取

image_crop(image, geometry="550x300+39+209") # 此处geometry参数的含义是:以宽40(39+1)、高210(209+1)的位置为将要截取出的小图片的左上角,截取宽550、高300的区域

image_crop(image, geometry="416x557+0+0") # 保留左半边,去掉右半边,也就是从"+0+0"(最左边,最上边)开始,截取宽为416,高为557的区域

image_crop(image, geometry="416x278+0+279") # 裁取图片左下角占图片四分之一的区域,其中"0+279"代表从最左边高是279的位置开始截取,"416x278"代表截取宽为416,高是278的矩形区域

image_crop(image, geometry="400x250+20+30", gravity="southeast") #设定gravity为"southeast"后,坐标原点变为右下角。我们从右下角向左移20,向上移30,截取宽为400,高为250的区域

## 注意:gravity的取值为"center"、"north"、"east"、"south"、"west","northeast"、"northwest"(默认值,即左上角)、"southeast"、"southwest"

image_crop(image, geometry="240x120+0+100", gravity="west") # 将gravity设为"west",此时geometry中"+0+0"表示左边中间位置,而"+0+100"和"+0-100"的效果一样,都表示向下移动100

#==========

# 练习:通过点击鼠标方便地进行截取

#==========

library(plothelper)

# image_crop_click(image) # 我们此时在画面上点击至少两次以便确定出矩形区域的四个边界,然后按Esc键,就可完成截取了,读者可通过查阅该函数的帮助了解如何截取不规则区域

## 尺寸调整

image_resize(image, "400x390!") # 宽为400,高为390

image_resize(image, "400x390") # 宽为400,高为268

# 这两行代码的差异在于,第1行代码加了"!",调整后图片的尺寸正好就是我们设定的尺寸,只是图片的宽和高发生了扭曲;第2行代码没有加"!",程序会尝试保持宽高比,使图片不扭曲,但尺寸不一定跟我们设定的相同

image_resize(image, "595x842!") # A4纸的比例

image_resize(image, "90%x120%!") # 两个数字后都加"%",会被理解为根据百分比进行调整

image_resize(image, "400x>450!") # ">450"的含义是,如果图片的高大于450,就将其调整为450,如果不大于450,就保持不变;"<"的含义与此相仿

image_resize(image, "400") # 宽调为400,高根据高宽比自动调整

image_resize(image, "400!") # 宽调为400,高保持不变

image_resize(image, "x400") # 高调为400(此处"x"前边什么都不写),宽自动调整

三、图片效果

plane=image_resize(image_read("plane yellow.jpg"), "25%x25%!") # 课件中的图片

## 转为黑白图片

image_convert(plane, colorspace="gray")

## 加入和减少噪声

image_noise(plane, noisetype="gaussian") # "gaussian"为高斯噪声(图1-3-1a),还可选择"multiplicative"、"impulse"、"laplacian"、"poisson"

image_reducenoise(plane, radius=8) # 减少噪声以产生模糊化效果,radius越大,效果越明显(图1-3-1b)

## 模糊化

image_blur(plane, radius=5, sigma=10) # radius和sigma的默认值为1和0.5,要增强效果必须同时增大它们的值(图1-3-1c)

image_median(plane, radius=10) # 中值模糊化(图1-3-1d)

## 油画效果

image_oilpaint(plane, radius=6) # radius越大,效果越明显,默认为1(图1-3-1e)

图1-3-1 左上=图a加入噪声,右上=图b减少噪声,左中=图c模糊化,右中=图d中值模糊化,左下=图e油画效果,右下=图f炭笔效果

## 炭笔效果

image_charcoal(plane, radius=1, sigma=0.5) # radius和sigma的默认值为1和0.5,要增强效果必须同时增大它们的值(图1-3-1f)

## 反色

image_negate(plane) #(图1-3-2a)

image_negate(image_convolve(plane, kernel="Do G:0, 0, 2")) # High Pass效果(图1-3-2b)

## 酸化效果

image_emboss(plane, radius=2, sigma=2) # 主要通过调节radius参数改变效果

## 内爆扭曲

image_implode(plane, factor=0.5) # factor为0至1的数值

## 添加半透明矩形

image_colorize(plane, opacity=30, color="purple") # 必须给出opacity和color,opacity的值越大效果越明显(图1-3-2c)

## 均衡化

image_equalize(plane)

## HSV调整

image_modulate(plane, brightness=120, saturation=120, hue=190) # brightness和saturation参数表示明度和饱和度相对于当前值的变化百分比;默认值都是100,也就是不发生变化;hue的默认值为100,取值范围为0至200,表示在色盘的当前位置向两边旋转的角度(因此,200就代表向后旋转180度)(图1-3-2d)

图1-3-2 左上=图a反色,右上=图b High Pass效果,左中=图c添加半透明矩形,右中=图d HSV调整,左下=图e减少颜色并使用黑白颜色,右下=图f将透明背景变为有色背景

## 减少图片中颜色的数量

image_quantize(plane, max=2) # 用max指定允许出现的颜色的数量(默认值为256)

image_quantize(plane, max=20, colorspace="gray") # 转为黑白图片并限制灰度的数量(图1-3-2e)

## 添加背景色

transp=image_read("plane yellow transparent.png") # 课件中的图片

image_background(transp, color="purple") #(图1-3-2f)

## 图片合并

# 不同的合并效果是通过设置image_composite函数中的operator参数实现的,我们可以用compose_types()来查看operator的可选项。以下列出了几种常用的操作

a=image_read("river.jpg") # 课件中的图片

a=image_resize(a, "600x400!")

b=image_read("plane transparent.png")

b=image_resize(b, "200x150!")

image_composite(a, b, operator="atop", offset="+200+125") # 将b置于a的上边,并且将a中用offset指定的点作为b的左上角(图1-3-3a)

image_composite(a, b, operator="atop", gravity="center") # 将b置于a的中间。请查看上文对gravity参数的解释

image_composite(a, b, operator="blend", compose_args="65", offset="+200+125") # operator="blend"代表图片融合,compose_args用字符设定,数值越小,b越不明显(图1-3-3b)

图1-3-3 左=图a atop合并,右=图b blend合并

# 先把a中与b尺寸相同的一个区域(在本例中是a的中心区域)截取出来,再用b的形状去截取这个区域

part=image_crop(a, "200x150+199+124")

image_composite(b, part, operator="in") # 飞机轮廓外变为透明(图1-3-4a) image_composite(b, part, operator="out") # 飞机轮廓内变为透明(图1-3-4b) # 现在将operator设为"dstout",从而保留a,但是让a由b中的图形占据的地方变为透明。注意:透明度在jpg格式的图片中是无效的,而本例中的a就是jpg格式的图片,因此我们必须首先将a转为png格式,同时用matte=TRUE打开透明度效果(图1-3-4c)

a_matte=image_convert(a, "png", matte=TRUE)

image_composite(a_matte, b, operator="dstout", offset="+200+125")

图1-3-4 左上=图a in合并,左下=图b out合并,右=图c dstout合并 njNjI3o7uCMsVfCdT+yopuMRIHyPOvv4AR6jn3Z4jG0mSmGFa/2FeJ1L0NhRgBr6

点击中间区域
呼出菜单
上一章
目录
下一章
×