用Python在Excel里画出蒙娜丽莎的方法示例


Posted in Python onApril 28, 2020

之前看到过很多头条,说哪国某人坚持了多少年自学使用excel画画,效果十分惊艳。
对于他们的耐心我十分敬佩。
但是作为一个程序员,自然也得挑战一下自己。
这种需求,我们十分钟就可以完成!

用Python在Excel里画出蒙娜丽莎的方法示例

基本思路

实现这个需求的基本思路是读取这张图片每一个像素的色彩值,然后给excel里的每一个单元格填充上颜色。所以主要用到的是PILopenpyxl这两个库。

PIL使用

PIL是Python里面做图像处理的时候十分常用的一个库,功能也是十分的强大,这里只需要用到PIL里一小部分的功能。

from PIL import Image
img = Image.open(img_path) # 读取图片
width, height = img.size # 获取图片大小
r, g, b = img.getpixel((w - 1, h - 1)) # 获取像素色彩值

Image.open()是PIL里面打开一张图片的函数,支持多种图片类型

img_path是图片路径,可以是相对路径,也可以是绝对路径

img.size是获取图片的size属性,包含图片的宽和高

img.getpixel() 是获取图片色彩值的函数,需传入一个tuplelist,值为像素坐标xy

openpyxl使用

openpyxl几乎是Python里功能最全的操作excel文件的库了,这里也只需要用到它的一小部分功能。

import openpyxl
from openpyxl.styles import fills

workbook = openpyxl.Workbook() 
worksheet = workbook.active
cell.fill = fills.PatternFill(fill_type="solid", fgColor=hex_rgb)
workbook.save(out_file)

openpyxl.Workbook() 新建一个excel文件

workbook.active 激活一个工作表

cell.fill = fills.PatternFill(fill_type="solid", fgColor=hex_rgb)填充一个单元格,fill_type="solid"是填充类型,fgColor=hex_rgb是填充的颜色

workbook.save()保存文件,需传入要保存的文件名

写一段代码

​ 写这一个画图的需求需要用到的核心就是上面介绍的PILopenpyxl的几种用法。但是在实际写的时候,还会有一些其他问题,比如:

getpixel()获取的颜色值是rgb十进制的,但fills.PatternFill 里的fgColor`参数接收到的颜色值是十六进制的值

这个问题其实就是十进制转十六进制,很容易解决

def int_to_16(num):
  num1 = hex(num).replace('0x', '')
  num2 = num1 if len(num1) > 1 else '0' + num1 # 位数只有一位的时候在前面补零
  return num2

excel的单元格默认是长方形,修改为正方形才不会使图片变形

if h == 1:
 _w = cell.column
 _h = cell.col_idx
 # 调整列宽
 worksheet.column_dimensions[_w].width = 1

# 调整行高
worksheet.row_dimensions[h].height = 6

这里用到了双重for循环,外层是width,里层是height,是一列一列的填充颜色,因此判断if h == 1,避免多次调整列宽。

excel支持的样式数量有限

这个问题比较严重。如果直接对高清大图进行操作,最后输出的excel文件在打开的时候,可能会提示我们文件有问题,需要自动修复。

但是等它修复完成之后,会发现填充的所有颜色都消失了!

用Python在Excel里画出蒙娜丽莎的方法示例

一开始以为是使用的行列数过多了原因。

查询资料后发现,13版excel支持的大行数是1048576,最大列数是16384,我们使用的单元格数量还远没达到限制。

在经过更换图片、更换excel版本,修改代码等不充分各种测试,才找到问题的原因所在。

原来是因为,excel的原形是由多个xml文件,填充的颜色都存储在一个style.xml文件里面,当这个文件过大就会导致打开的时候报错。

所以为了解决这个问题,有两个解决方案,第一是缩小图片,第二是减少图片颜色。缩小图片的时候自带减少图片颜色的功能,减少图片颜色的方法可以采用灰度化、二值化等方法。

总体上来讲,就是需要控制颜色数量*单元格数<阈值(3300w左右)

MAX_WIDTH = 300
MAX_HEIGHT = 300
def resize(img):
  w, h = img.size
  if w > MAX_WIDTH:
    h = MAX_WIDTH / w * h
    w = MAX_WIDTH

  if h > MAX_HEIGHT:
    w = MAX_HEIGHT / h * w
    h = MAX_HEIGHT
  return img.resize((int(w), int(h)), Image.ANTIALIAS)

最终效果

苍天不负有心人,打开最后输出的excel已经可以看到效果了!

所以说,一切能用Python解决的问题,最终都会用Python来解决。

用Python在Excel里画出蒙娜丽莎的方法示例

全部代码

# draw_excel.py

from PIL import Image
import openpyxl
from openpyxl.styles import fills
import os

MAX_WIDTH = 300
MAX_HEIGHT = 300

def resize(img):
  w, h = img.size
  if w > MAX_WIDTH:
    h = MAX_WIDTH / w * h
    w = MAX_WIDTH

  if h > MAX_HEIGHT:
    w = MAX_HEIGHT / h * w
    h = MAX_HEIGHT
  return img.resize((int(w), int(h)), Image.ANTIALIAS)


def int_to_16(num):
  num1 = hex(num).replace('0x', '')
  num2 = num1 if len(num1) > 1 else '0' + num1
  return num2


def draw_jpg(img_path):

  img_pic = resize(Image.open(img_path))
  img_name = os.path.basename(img_path)
  out_file = './result/' + img_name.split('.')[0] + '.xlsx'
  if os.path.exists(out_file):
    os.remove(out_file)

  workbook = openpyxl.Workbook()
  worksheet = workbook.active

  width, height = img_pic.size

  for w in range(1, width + 1):

    for h in range(1, height + 1):
      if img_pic.mode == 'RGB':
        r, g, b = img_pic.getpixel((w - 1, h - 1))
      elif img_pic.mode == 'RGBA':
        r, g, b, a = img_pic.getpixel((w - 1, h - 1))

      hex_rgb = int_to_16(r) + int_to_16(g) + int_to_16(b)

      cell = worksheet.cell(column=w, row=h)

      if h == 1:
        _w = cell.column
        _h = cell.col_idx
        # 调整列宽
        worksheet.column_dimensions[_w].width = 1
      # 调整行高
      worksheet.row_dimensions[h].height = 6
      
      cell.fill = fills.PatternFill(fill_type="solid", fgColor=hex_rgb)

    print('write in:', w, ' | all:', width + 1)
  print('saving...')
  workbook.save(out_file)
  print('success!')

if __name__ == '__main__':
  draw_jpg('mona-lisa.jpg')

附:

上面说到 颜色数量*单元格数<阈值(256^4左右)的时候,可能有人会有疑惑,这个256^4是怎么来的。

这个当然是我测试信口开河得来的。

既然是有颜色数量单元格数这两个变量,那么自然要有两个测试方法以上。一个观察颜色数量,一个观察单元格数

但我在这里只做了颜色数量的一个测试。(最大的原因就是生成上万行*上万列的excel实在是太久了...懒...)

count = 0
  MAX_WIDTH = 255
  for w in range(1, MAX_WIDTH + 1):
    for h in range(1, MAX_WIDTH + 1):
      cell = worksheet.cell(column=w, row=h)
      if h == 1:
        _w = cell.column
        _h = cell.col_idx
        # 调整列宽
        worksheet.column_dimensions[_w].width = 1
      # 调整行高
      worksheet.row_dimensions[h].height = 6
      
      if count < 255 ** 3:
        back = int_to_16(num=count)
        back = '0' * (6 - len(back)) + back
      else:
        back = ''.join([int_to_16(random.randint(0, 255)) for _ in range(3)])
      cell.fill = fills.PatternFill(fill_type="solid", fgColor=back)
      count += 1

count是记录颜色的变量,确保每一个颜色都不重复,但目前计算机RGB表示的颜色最多只有256^3种

通过调整MAX_WIDTH的值来测试excel的阈值

最后生成的测试excel如下:

用Python在Excel里画出蒙娜丽莎的方法示例

...还有点好看。!??

最后

由于精力有限,能力也有限 ,所以没有测试单一颜色的,也可能有其他的方法能没有这个阈值的限制。

代码已经全部上传至github,欢迎大家友好交流讨论 [握手]

到此这篇关于用Python在Excel里画出蒙娜丽莎的方法示例的文章就介绍到这了,更多相关Python Excel画蒙娜丽莎内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python修改操作系统时间的方法
May 18 Python
Python3读取UTF-8文件及统计文件行数的方法
May 22 Python
Python编程实现的简单Web服务器示例
Jun 22 Python
python利用正则表达式排除集合中字符的功能示例
Oct 10 Python
python使用opencv按一定间隔截取视频帧
Mar 06 Python
python email smtplib模块发送邮件代码实例
Apr 26 Python
完美解决Python 2.7不能正常使用pip install的问题
Jun 12 Python
使用Python制作表情包实现换脸功能
Jul 19 Python
计算pytorch标准化(Normalize)所需要数据集的均值和方差实例
Jan 15 Python
Python多线程threading创建及使用方法解析
Jun 17 Python
python 逐步回归算法
Apr 06 Python
用Python仅20行代码编写一个简单的端口扫描器
Apr 08 Python
全网首秀之Pycharm十大实用技巧(推荐)
Apr 27 #Python
python实时监控logstash日志代码
Apr 27 #Python
python实现秒杀商品的微信自动提醒功能(代码详解)
Apr 27 #Python
浅析python 定时拆分备份 nginx 日志的方法
Apr 27 #Python
python异步Web框架sanic的实现
Apr 27 #Python
python库skimage给灰度图像染色的方法示例
Apr 27 #Python
python实现密度聚类(模板代码+sklearn代码)
Apr 27 #Python
You might like
深入php常用函数的使用汇总
2013/06/08 PHP
PHP如何通过AJAX方式实现登录功能
2015/11/23 PHP
php判断是否连接上网络的方法实例详解
2016/12/14 PHP
PHP+RabbitMQ实现消息队列的完整代码
2019/03/20 PHP
Laravel 5.5 实现禁用用户注册示例
2019/10/24 PHP
浏览器加载、渲染和解析过程黑箱简析
2012/11/29 Javascript
用unescape反编码得出汉字示例
2014/04/24 Javascript
jquery操作select元素和option的实例代码
2016/02/03 Javascript
node.js中使用Export和Import的方法
2017/09/18 Javascript
原生实现一个react-redux的代码示例
2018/06/08 Javascript
JS实现获取自定义属性data值的方法示例
2018/12/19 Javascript
JS实现移动端点击按钮复制文本内容
2019/07/28 Javascript
解决mui框架中switch开关通过js控制开或者关状态时小圆点不动的问题
2019/09/03 Javascript
解决vue更新路由router-view复用组件内容不刷新的问题
2019/11/04 Javascript
vue输入框使用模糊搜索功能的实现代码
2020/05/26 Javascript
[06:25]第二届DOTA2亚洲邀请赛主赛事第二天比赛集锦.mp4
2017/04/03 DOTA
用python实现的可以拷贝或剪切一个文件列表中的所有文件
2009/04/30 Python
python多线程操作实例
2014/11/21 Python
Python中解析JSON并同时进行自定义编码处理实例
2015/02/08 Python
python搭建虚拟环境的步骤详解
2016/09/27 Python
Python使用matplotlib绘制正弦和余弦曲线的方法示例
2018/01/06 Python
python 编码规范整理
2018/05/05 Python
python用列表生成式写嵌套循环的方法
2018/11/08 Python
Django--权限Permissions的例子
2019/08/28 Python
python3中rank函数的用法
2019/11/27 Python
Pycharm如何运行.py文件的方法步骤
2020/03/03 Python
pycharm安装及如何导入numpy
2020/04/03 Python
在python中修改.properties文件的操作
2020/04/08 Python
平面设计求职信
2014/03/10 职场文书
2015小学毕业班工作总结
2015/07/21 职场文书
2016党校学习心得体会范文
2016/01/07 职场文书
《惊弓之鸟》教学反思
2016/02/20 职场文书
python 遍历磁盘目录的三种方法
2021/04/02 Python
Vue实现导入Excel功能步骤详解
2021/07/03 Vue.js
Pygame Draw绘图函数的具体使用
2021/11/17 Python
分享Python获取本机IP地址的几种方法
2022/03/17 Python