用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中bisect的用法
Sep 23 Python
给Python IDLE加上自动补全和历史功能
Nov 30 Python
探究数组排序提升Python程序的循环的运行效率的原因
Apr 01 Python
在Python中使用元类的教程
Apr 28 Python
python3实现读取chrome浏览器cookie
Jun 19 Python
python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解
Nov 24 Python
python cs架构实现简单文件传输
Mar 20 Python
python 实现将txt文件多行合并为一行并将中间的空格去掉方法
Dec 20 Python
浅谈ROC曲线的最佳阈值如何选取
Feb 28 Python
PyCharm 2020 激活到 2100 年的教程
Mar 25 Python
零基础学python应该从哪里入手
Aug 11 Python
Python Selenium自动化获取页面信息的方法
Aug 31 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下过滤HTML代码的函数
2007/12/10 PHP
PHP获取浏览器信息类和客户端地理位置的2个方法
2014/04/24 PHP
php 判断网页是否是utf8编码的方法
2014/06/06 PHP
Yii框架数据模型的验证规则rules()被执行的方法
2016/12/02 PHP
详细分析PHP 命名空间(namespace)
2020/06/30 PHP
修改jQuery.Autocomplete插件 支持中文输入法 避免TAB、ENTER键失效、导致表单提交
2009/10/11 Javascript
体验js中splice()的强大(插入、删除或替换数组的元素)
2013/01/16 Javascript
Javasipt:操作radio标签详解
2013/12/30 Javascript
javascript中解析四则运算表达式的算法和示例
2014/08/11 Javascript
JsRender for object语法简介
2014/10/31 Javascript
Bootstrap学习笔记之css样式设计(1)
2016/06/07 Javascript
Angular.JS判断复选框checkbox是否选中并实时显示
2016/11/30 Javascript
nodejs读写json文件的简单方法(必看)
2017/03/09 NodeJs
基于vue+canvas的excel-like组件实例详解
2017/11/28 Javascript
从0到1构建vueSSR项目之node以及vue-cli3的配置
2019/03/07 Javascript
vue canvas绘制矩形并解决由clearRec带来的闪屏问题
2019/09/02 Javascript
vue ajax 拦截原理与实现方法示例
2019/11/29 Javascript
使用Python的Scrapy框架十分钟爬取美女图
2016/12/26 Python
基于Django contrib Comments 评论模块(详解)
2017/12/08 Python
tensorflow更改变量的值实例
2018/07/30 Python
python 列表中[ ]中冒号‘:’的作用
2019/04/30 Python
Python 数据可视化pyecharts的使用详解
2019/06/26 Python
Flask模板引擎之Jinja2语法介绍
2019/06/26 Python
通过python实现弹窗广告拦截过程详解
2019/07/10 Python
Pyorch之numpy与torch之间相互转换方式
2019/12/31 Python
Python如何使用字符打印照片
2020/01/03 Python
浅谈keras 的抽象后端(from keras import backend as K)
2020/06/16 Python
python的json包位置及用法总结
2020/06/21 Python
localStorage 设置过期时间的方法实现
2018/12/21 HTML / CSS
使用Canvas操作像素的方法
2018/06/14 HTML / CSS
2014年母亲节寄语
2014/05/07 职场文书
食品安全处置方案
2014/06/14 职场文书
基层党员对照检查材料
2014/08/25 职场文书
模范教师材料大全
2014/12/16 职场文书
公司员工辞职信范文
2015/05/12 职场文书
解决Mysql中的innoDB幻读问题
2022/04/29 MySQL