内容提要:
* 加载和准备数据
* 训练 U-net 模型
* 测试模型
* 自动去除背景
点击蓝字 |关注我们
前段时间小编我回家twitter冲浪,突然发现Rblog上贴了一个包含U-net模型的platypus包。U-net正好可以做图像分割,去除背景的任务。
我们今天就用platypus包内的U-net做一个换景的小案例。
流程大致是这样:
可能有伙伴好奇,为啥这个包叫platypus呢,platpus是啥?
当然是鸭嘴兽呀,这可是号称上古遗兽的家伙,而且非常可爱,不信,你瞅瞅。
知道了要是用的R包,下一步就是数据了。
为了训练模型,我们需要一组训练数据集。首先讲一下咱们拿到手的是什么数据。
训练数据集中的图是成对出现的,一张是包含背景和前景的正常图片;另一张是涂抹掉背景,只留下前景的被分割轮廓图。
说的这么复杂,不如看图,各位小伙伴,一看就明白了,不就是抠图么
加载和准备数据
Loading and Preparing
这里,我们使用的是牛津大学宠物数据集中的猫狗两类。
当然,小伙伴们自己练习的时候可以随便选取,注意保留对应的掩码文件就可以啦。
掩码图片:就是指涂抹掉背景,只留下前景的被分割轮廓图,是对原始数据图的一种标注,一般需要人工标注,无法依靠计算获得。
数据集,小编我已经找好了,小伙伴们在后台发送“图形分割”来获取哦~
table(dir("oxford_pets/annotations_jpg/") %in% dir("oxford_pets/images/"))table(dir("oxford_pets/images/") %in% dir("oxford_pets/annotations_jpg/"))
把宠物数据里的图片和掩码图片整理好后,先要验证一下图片和掩码图片是不是一一对应。
我们可以做个随机检查,比如查看一下 image1 与 mask1:
image1 <- readJPEG("oxford_pets/images/Abyssinian_2.jpg")plot(as.raster(image1))mask1 <- readJPEG("oxford_pets/annotations_jpg/Abyssinian_2.jpg")plot(as.raster(mask1))
训练 U-net 模型
Training
初始化 U-net 模型,其实就是定义和编译网络模型,这个和 keras 很像。参数嘛,用默认的即可。
batch_size <- 32size <- 256n_training <- length(dir("oxford_pets/images/"))test_unet <- u_net(size,size,grayscale = F,blocks = 4,n_class = 2)test_unet %>% compile(optimizer = optimizer_adam(lr=0.001),loss = loss_dice(),metrics = metric_dice_coeff())test_unet
不过,由于网络结构太大了,无奈之下,小编我只能截取部分给大家看一下了:
定义好网络之后,咱们就要使用猫狗数据来“炼丹”啦。
先把供料装置安装起来:
datagen <- segmentation_generator(path = "oxford_pets",colormap = trinity_colormap,only_images = F,mode = "dir",net_h = size, net_w = size,grayscale = F,batch_size = batch_size,shuffle = F,subdirs = c("/images", "/annotations_jpg"))
若要区分背景图、前景图和边界线,需要自定义 segmention_generator 函数中的 colormap 参数。
现在,一切准备就绪,咱们开炉炼丹!
小编我使用的是笔记本,所以迭代次数我就搞个5吧,我怕我小电脑冒烟......
history <- test_unet %>%fit_generator(datagen,epochs = 5,steps_per_epoch = n_training %/% batch_size,verbose = 2)
如果小伙伴们使用过 Rstudio 下的 keras,那一定很熟悉炼丹过程中,生成的过程图:
迭代了5次之后,loss就比较小了,dice coefficient 大约是0.9,这个结果其实还是说的过去的。
loss大家都比较熟悉,这里就解释一下dice coefficient。这个因子用来对比预测的掩码图片和实际掩码图片的相似度,即调和平均精度,harmonic mean of precision,其实英文更加容易理解😮
虽然只训练了5次,但好不容易也练出了一颗丹,自然要保存一下。
此外,悄悄地说一声,加载模型的方法,小编我写在了保存的模型下面,大家按需食用。
save_model_weights_tf(test_unet, "unet_model_weights")load_model_weights_tf("unet_model_weights")
测试模型
Testing
到这里,其实拿上面的模型就可以做许多东西了。
但是,为了呼应开头,我们还是 “画” 图片的掩码,去除图片背景。
我找了只鸟的图片。训练的猫狗,让找鸟,貌似不是很合适,但是手里只有这么一张图了
bird <- image_load("birds_folder/starling female/020.jpg", target_size = c(size,size))bird %>%image_to_array() %>%`/`(255) %>%as.raster() %>%plot()
把上面加载进来的鸟儿输入到模型,得到掩码图片:
x <- bird %>%image_to_array() %>%array_reshape(., c(1, dim(.))) %>%`/`(255)mask <- test_unet %>% predict(x) %>%get_masks(binary_colormap)plot(as.raster(mask[[1]]/255))
emmmm 模型出的结果不是很好,把鸟儿脚下的木头也给识别成小鸟的一部分了。
主要是由于模型的前景色和背景色相近导致的样。大家在训练的时候,可以构建更加复杂的网络,以提高模型对图形的识别能力。
自动去除背景
Removing
现在,咱们可以写个小函数,从读取图片、生成掩码图片到换景一气呵成地做完。
即通过训练好的模型,得到掩码图片,然后贴到你喜欢的背景图上,实现换景操作。
change_background <- function(image, background){img <- image_load(image, target_size = c(size,size))x <- img %>%image_to_array() %>%array_reshape(., c(1, dim(.))) %>%`/`(255)mask <- test_unet %>% predict(x) %>%get_masks(binary_colormap)mask_blur <- magick::image_read(mask[[1]]/255) %>%image_blur(20,5)img_crop <- image_composite(image_read(image_to_array(img)/255), mask_blur,operator = "CopyOpacity")background <- image_read(background)image_composite(background, img_crop, gravity = "center")}
咱们用的这个是猴面鹰,也称作仓鸮(xiao),是国家二级保护动物,和猫头鹰的习性类似。
我们用大海做背景图:
咱们现在把原图和背景图放到刚才自编的小函数中:
change_background(image = "birds/owl/612.jpg",background = "backgrounds/random_water.jpg")
然后,就得到在大海上翱翔的猴面鹰了。
效果貌似有点不理想,只是给大家提供个指引。小伙伴们做的时候,可以通过下面几个小Tips提高 ”换景“ 的效果噢:
训练的时间长一点;
训练背景、前景、和边界三类图作为原始用图,并在此基础上使用一些模糊、透明等函数处理;
可以使用 R-CNN 来代替 U-Net 网络。
参考文献
Image segmentation in R: Automatic background removal like in a Zoom conference
欢迎大家关注简博士的B站和公众号,在公众号私信“入群”,可以与小伙伴们一起讨论问题哦。
扫码关注我们
微信号|Dr_Janneil
B站|简博士

