用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实现strcmp函数功能示例
Mar 25 Python
Python实现的HTTP并发测试完整示例
Apr 23 Python
简单介绍Python中的decode()方法的使用
May 18 Python
Python正规则表达式学习指南
Aug 02 Python
Python正确重载运算符的方法示例详解
Aug 27 Python
python实现年会抽奖程序
Jan 22 Python
python实现图片插入文字
Nov 26 Python
centos7中安装python3.6.4的教程
Dec 11 Python
你可能不知道的Python 技巧小结
Jan 29 Python
解决pycharm不能自动补全第三方库的函数和属性问题
Mar 12 Python
PYcharm 激活方法(推荐)
Mar 23 Python
Python生成pdf目录书签的实例方法
Oct 29 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 图像函数大举例(非原创)
2009/06/20 PHP
通过修改配置真正解决php文件上传大小限制问题(nginx+php)
2015/09/23 PHP
PHP中include和require的区别实例分析
2017/05/07 PHP
jquery创建div 实现代码
2009/04/27 Javascript
JS维吉尼亚密码算法实现代码
2010/11/09 Javascript
jQuery中的.bind()、.live()和.delegate()之间区别分析
2011/06/08 Javascript
jquery为页面增加快捷键示例
2014/01/31 Javascript
js实现按钮加背景图片常用方法
2014/11/01 Javascript
JavaScript整除运算函数ceil和floor的区别分析
2015/04/14 Javascript
JavaScript数组和循环详解
2015/04/27 Javascript
Angularjs 动态添加指令并绑定事件的方法
2017/04/13 Javascript
mui 打开新窗口的方式总结及注意事项
2017/08/20 Javascript
客户端(vue框架)与服务器(koa框架)通信及服务器跨域配置详解
2017/08/26 Javascript
JS中的事件委托实例浅析
2018/03/22 Javascript
微信小程序项目总结之记账小程序功能的实现(包括后端)
2019/08/20 Javascript
Vue SPA 首屏优化方案
2021/02/26 Vue.js
[02:30]DOTA2放量测试专访海涛:呼吁保护新手玩家
2013/08/26 DOTA
python3使用tkinter实现ui界面简单实例
2014/01/10 Python
pyqt4教程之实现windows窗口小示例分享
2014/03/07 Python
Python语言的面相对象编程方式初步学习
2016/03/12 Python
python样条插值的实现代码
2018/12/17 Python
python常用库之NumPy和sklearn入门
2019/07/11 Python
TensorFlow查看输入节点和输出节点名称方式
2020/01/04 Python
Python读取配置文件(config.ini)以及写入配置文件
2020/04/08 Python
如何用Python和JS实现的Web SSH工具
2021/02/23 Python
突袭HTML5之Javascript API扩展3—本地存储全新体验
2013/01/31 HTML / CSS
斯凯奇澳大利亚官网:SKECHERS澳大利亚
2018/03/31 全球购物
匡威西班牙官网:Converse西班牙
2019/10/01 全球购物
初中班主任经验交流材料
2014/05/16 职场文书
无毒社区工作方案
2014/05/23 职场文书
市场营销计划书范文
2015/01/16 职场文书
2015年纪念“卢沟桥事变”78周年活动方案
2015/05/06 职场文书
故意杀人案辩护词
2015/05/21 职场文书
公务员处分决定书
2015/06/25 职场文书
如何制定一份可行的计划!
2019/06/21 职场文书
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
2021/06/30 SQL Server