R 语言绘制肖像画

简介: R 语言绘制肖像画

简介

最近在网上冲浪🏄时,发现了这样一个博客[1],该博客使用层次聚类方法绘制简笔画,完整代码见仓库[2]

思想:加载照片后,使用 imager 包中的 thresold() 函数将值转化为黑白图像。之后,随机采样样本并使用聚类算法得到聚类点并进行连接。

教程

安装包

读者需要安装以下包:

install.packages("imager")
install.packages("tidyverse")
install.packages("purrr")

加载与处理图形

library(imager)
library(tidyverse)
library(purrr)
# 照片位置设定
file="img/frankenstein.jpg"


加载图形,转换为灰度,并过滤和采样图像。

load.image(file) %>% 
  grayscale() %>% 
  threshold("45%") %>% 
  as.cimg() %>% 
  as.data.frame() -> franky

相关函数

  1. clustResultx:切割树突,并移除仅由一个点形成的簇
clustResultx <- function(x) {
  clustCut <- tibble(cluster_id = cutree(clusters, x)) %>% bind_cols(data)
  clustCut %>% group_by(cluster_id) %>% 
    summarize(size = n()) %>% 
    filter(size > 1) %>% 
    select(cluster_id) %>% 
    inner_join(clustCut, by = "cluster_id") -> clustCut
  return(clustCut)
  }
  1. add_segments:添加比较两个连续聚类的结果
add_segments <- function(x){
  df1 <- clustEvol[[x]]
  df0 <- clustEvol[[x-1]]
  new_points <- anti_join(df1, df0, by = "id")
  # If a new point is added to an existing cluster
  new_points %>% 
    inner_join(df1, by = "cluster_id", suffix = c(".1", ".2")) %>% 
    filter(id.1 != id.2) %>% 
    mutate(d = sqrt((x.1 - x.2)^2 + (y.1 - y.2)^2)) %>% 
    group_by(id.1) %>% 
    arrange(d) %>% 
    slice(1) %>% 
    select(p1 = id.1, p2 = id.2) %>% 
    ungroup -> new_segments1
  # If a new 2-points cluster is generated
  new_points %>% anti_join(bind_rows(select(new_segments1, id = p1), 
                                     select(new_segments1, id = p2)), by = "id") %>% 
    group_by(cluster_id) %>% 
    ungroup -> unpaired_points
  unpaired_points %>% inner_join(unpaired_points, by = "cluster_id", suffix = c(".1", ".2")) %>% 
    filter(id.1 < id.2) %>% 
    select(p1 = id.1, p2 = id.2) -> new_segments2
  # If two existing clusters are joined
  new_points <- anti_join(df1, df0, by = c("id", "cluster_id"))
  new_points %>% 
    inner_join(df1, by = "cluster_id", suffix = c(".1", ".2")) %>% 
    filter(id.1 != id.2) %>% 
    anti_join(new_points, by = c("id.2" = "id")) %>% 
    mutate(d = sqrt((x.1 - x.2)^2 + (y.1 - y.2)^2)) %>% 
    arrange(d) %>% 
    slice(1) %>% 
    select(p1 = id.1, p2 = id.2) %>% 
    ungroup -> new_segments3
  bind_rows(new_segments1, new_segments2, new_segments3)
}

算法应用

# 样本大小
n <- 2500
# 随机抽取图形中的样本
franky %>% 
  sample_n(n, weight=(1-value)) %>% 
  select(x,y) %>% mutate(id = row_number()) -> data
# 各点之间距离计算
dist_data <- dist(data %>% select(-id), method = "euclidean")
# 分层聚类
clusters <- hclust(dist_data, method = 'single')
# 列出所有可能的集群,从大到小
nrow(data):1 %>% 
  map(function(x) clustResultx(x)) -> clustEvol
# Segments of clusters
2:length(clustEvol) %>% 
  map(function(x) add_segments(x)) %>% 
  bind_rows() -> segments_id
# Segments in (x, y) and (xend, yend) format
segments_id %>% 
  inner_join(data, by = c("p1" = "id"), suffix = c(".1", ".2")) %>% 
  inner_join(data, by = c("p2" = "id"), suffix = c(".1", ".2")) %>% 
  select(x = x.1, y = y.1, xend = x.2, yend = y.2) -> segments

注意:运行本文所有代码,大概需要花费3-4分钟。请耐性等待~

绘图

ggplot(segments) + 
  geom_curve(aes(x = x, y = y, xend = xend, yend = yend),
             ncp = 10) +
  scale_x_continuous(expand=c(.1, .1)) +
  scale_y_continuous(expand=c(.1, .1), trans = scales::reverse_trans()) +
  coord_equal() +
  theme_void()

通过修改采样样本数量可以得到不同的图形,例如:

其他例子

小编使用上面代码尝试了其他图形,结果如下:


参考资料

[1]

博客: https://fronkonstin.com/2019/08/05/clustering-frankenstein/

[2]

仓库: https://github.com/aschinchon/clustering-frankenstein

目录
相关文章
|
存储 设计模式 前端开发
Streamlit应用中构建多页面(三):两种方案(上)
Streamlit应用中构建多页面(三):两种方案
4243 0
|
11月前
|
数据采集 数据可视化 数据挖掘
金融波动率的多模型建模研究:GARCH族与HAR模型的Python实现与对比分析
本文探讨了金融资产波动率建模中的三种主流方法:GARCH、GJR-GARCH和HAR模型,基于SPY的实际交易数据进行实证分析。GARCH模型捕捉波动率聚类特征,GJR-GARCH引入杠杆效应,HAR整合多时间尺度波动率信息。通过Python实现模型估计与性能比较,展示了各模型在风险管理、衍生品定价等领域的应用优势。
948 66
金融波动率的多模型建模研究:GARCH族与HAR模型的Python实现与对比分析
|
机器学习/深度学习 人工智能 自然语言处理
什么是多层感知器(MLP)?
【8月更文挑战第23天】
2385 0
|
存储 人工智能 自然语言处理
新手指南:微软ai助手Copilot国内如何使用?
微软 Copilot 是一款强大的 AI 助手,掌握一些技巧可以让你更好地利用它,提高效率和创造力,让你的工作和生活更加精彩!
13004 11
|
监控 程序员 持续交付
`pylint`是一个高度可配置的Python代码分析工具,它可以帮助程序员查找代码中的错误、样式问题、可能的bug以及不符合编码标准的部分。
`pylint`是一个高度可配置的Python代码分析工具,它可以帮助程序员查找代码中的错误、样式问题、可能的bug以及不符合编码标准的部分。
|
供应链 安全 API
2024攻防演练:4大趋势凸显,如何做好常态化安全防御?
2024年全国性攻防演练加剧,呈现四大趋势:0day漏洞转攻供应链,攻击手段多元化,工具更隐蔽智能,API接口成主要目标。瑞数信息研究员陆攀建议企业采取四大防御策略:缩小攻击面,加强供应链安全,提升社工安全意识,及建立0day防御体系。企业需构建常态化的安全防御,以应对日益复杂的网络安全挑战。
551 0
|
测试技术 Android开发
Android App获取不到pkgInfo信息问题原因
Android App获取不到pkgInfo信息问题原因
1015 0
|
Serverless
统计问题|绘制任意分布的 QQ 图
统计问题|绘制任意分布的 QQ 图
444 1
|
数据可视化
R可视乎|三维散点图
R可视乎|三维散点图
363 0
|
数据可视化
R可视乎|主成分分析结果可视化
R可视乎|主成分分析结果可视化
316 0