python图像彩色数字化
效果展示:

目录结构:

alphabets.py
GENERAL = {"simple": "@%#*+=-:. ","complex": "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
}
# Full list could be found here https://github.com/tsroten/zhon/tree/develop/zhon/cedict
CHINESE = {"standard": "龘䶑瀰幗獼鑭躙䵹觿䲔釅欄鐮䥯鶒獭鰽襽螻鰱蹦屭繩圇婹歜剛屧磕媿慪像僭堳噞呱棒偁呣塙唑浠唼刻凌咄亟拮俗参坒估这聿布允仫忖玗甴木亪女去凸五圹亐囗弌九人亏产斗丩艹刂彳丬了5丄三亻讠厂丆丨1二宀冖乛一丶、",
}KOREAN = {"standard": "ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㅏㅑㅓㅕㅗㅛㅜㅠㅡㅣ"
}JAPANESE = {"hiragana": "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん","katakana": "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン"
}ENGLISH = {"standard": "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
}RUSSIAN = {"standard": "АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя"
}GERMAN = {"standard": "AaÄäBbßCcDdEeFfGgHhIiJjKkLlMmNnOoÖöPpQqRrSsTtUuÜüVvWwXxYyZz"
}FRENCH = {"standard": "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÆæŒœÇçÀàÂâÉéÈèÊêËëÎîÎïÔôÛûÙùŸÿ"
}SPANISH = {"standard": "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÑñáéíóú¡¿"
}ITALIAN = {"standard": "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÀÈàèéìòù"
}PORTUGUESE = {"standard": "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzàÀáÁâÂãÃçÇéÉêÊíÍóÓôÔõÕúÚ"
}POLISH = {"standard": "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpRrSsTtUuWwYyZzĄąĘęÓóŁłŃńŻżŚśĆ揟"
}
app.py
import os
import hashlib
import argparse
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageOps
from flask import Flask, request, send_file, render_template_string
from utils import get_dataapp = Flask(__name__)# 确保上传和输出目录存在
if not os.path.exists("uploads"):os.makedirs("uploads")
if not os.path.exists("outputs"):os.makedirs("outputs")# 文件处理常量
ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif', 'bmp'}
MAX_FILE_SIZE = 5 * 1024 * 1024 # 最大文件大小为5MBdef get_args():"""获取命令行参数"""parser = argparse.ArgumentParser("Image to ASCII")parser.add_argument("--input", type=str, default="uploads/input.jpg", help="输入图片路径")parser.add_argument("--output", type=str, default="outputs/output.jpg", help="输出图片路径")parser.add_argument("--language", type=str, default="english") # 使用的字符语言parser.add_argument("--mode", type=str, default="standard") # ASCII 模式parser.add_argument("--background", type=str, default="black", choices=["black", "white"],help="背景颜色")parser.add_argument("--num_cols", type=int, default=300, help="输出图片的宽度字符数")parser.add_argument("--scale", type=int, default=2, help="输出图片的缩放比例")args = parser.parse_args()return argsdef md5_filename(filename):"""返回文件名的MD5哈希值,用于生成唯一的文件名"""hash_md5 = hashlib.md5()hash_md5.update(filename.encode('utf-8')) # 更新哈希值return hash_md5.hexdigest()def allowed_file(filename):"""检查文件是否具有允许的扩展名"""return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONSdef convert_image_to_ascii(opt):"""将图像转换为ASCII字符图像:param opt: 命令行参数对象,包含转换的配置"""# 根据背景颜色设置背景if opt.background == "white":bg_code = (255, 255, 255)else:bg_code = (0, 0, 0)# 获取字符列表、字体和缩放参数char_list, font, sample_character, scale = get_data(opt.language, opt.mode)num_chars = len(char_list)num_cols = opt.num_cols# 检查输入文件是否存在if not os.path.exists(opt.input):print(f"错误:文件 {opt.input} 不存在!")return# 读取图像并验证image = cv2.imread(opt.input, cv2.IMREAD_COLOR)if image is None:print(f"错误:无法加载图片 {opt.input}")return# 将图像从BGR格式转换为RGB格式image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 获取图像尺寸height, width, _ = image.shapecell_width = width / opt.num_colscell_height = scale * cell_widthnum_rows = int(height / cell_height)# 如果列数或行数过多,使用默认设置if num_cols > width or num_rows > height:print("列数或行数过多,使用默认设置。")cell_width = 6cell_height = 12num_cols = int(width / cell_width)num_rows = int(height / cell_height)# 获取字符的宽度和高度char_width, char_height = font.getsize(sample_character)out_width = char_width * num_colsout_height = scale * char_height * num_rows# 创建输出图像out_image = Image.new("RGB", (out_width, out_height), bg_code)draw = ImageDraw.Draw(out_image)# 逐个处理图像区域,转换为字符for i in range(num_rows):for j in range(num_cols):partial_image = image[int(i * cell_height):min(int((i + 1) * cell_height), height),int(j * cell_width):min(int((j + 1) * cell_width), width), :]partial_avg_color = np.sum(np.sum(partial_image, axis=0), axis=0) / (cell_height * cell_width)partial_avg_color = tuple(partial_avg_color.astype(np.int32).tolist())# 根据平均色值选择合适的字符char = char_list[min(int(np.mean(partial_image) * num_chars / 255), num_chars - 1)]draw.text((j * char_width, i * char_height), char, fill=partial_avg_color, font=font)# 根据背景颜色裁剪图像if opt.background == "white":cropped_image = ImageOps.invert(out_image).getbbox()else:cropped_image = out_image.getbbox()out_image = out_image.crop(cropped_image)out_image.save(opt.output)@app.route('/')
def index():"""渲染首页HTML,用户可以上传图片"""return render_template_string("""<html><head><style>body {font-family: Arial, sans-serif;background-color: #f4f4f9;margin: 0;padding: 0;display: flex;justify-content: center;align-items: center;height: 100vh;}.container {background-color: #fff;padding: 30px;border-radius: 8px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);width: 300px;text-align: center;}h1 {font-size: 24px;color: #333;}form {margin-top: 20px;}input[type="file"] {display: block;margin: 10px auto;padding: 10px;border: 1px solid #ccc;border-radius: 4px;width: 100%;}input[type="submit"] {background-color: #4CAF50;color: white;border: none;padding: 12px 20px;border-radius: 4px;cursor: pointer;width: 100%;font-size: 16px;}input[type="submit"]:hover {background-color: #45a049;}.footer {margin-top: 20px;font-size: 14px;color: #777;}</style></head><body><div class="container"><h1>彩色图片转化器YFREE</h1><form action="/upload" method="POST" enctype="multipart/form-data"><input type="file" name="image" required><input type="submit" value="上传图片"></form><div class="footer"><p>支持开源 <a href="https://github.com/vietnh1009/ASCII-generator" target="_blank">github</a></p></div></div></body></html>""")@app.route('/upload', methods=['POST'])
def upload():"""处理上传的图片,将其转换为ASCII图像并返回"""if 'image' not in request.files:return '没有文件上传'file = request.files['image']if file.filename == '':return '没有选择文件'# 检查文件扩展名if not allowed_file(file.filename):return '无效的文件类型,请上传图片。'# 检查文件大小file.seek(0, os.SEEK_END)file_size = file.tell()if file_size > MAX_FILE_SIZE:return '文件太大,最大支持文件大小为5MB。'file.seek(0) # 重置文件指针# 保存文件md5_filename_value = md5_filename(file.filename)filename = os.path.join("uploads", md5_filename_value + os.path.splitext(file.filename)[1])file.save(filename)if not os.path.exists(filename):return '文件上传失败。'# 获取参数并处理图像opt = get_args()opt.input = filenameopt.output = os.path.join("outputs", f"output_{md5_filename_value}.jpg")# 调用转换函数convert_image_to_ascii(opt)return send_file(opt.output, as_attachment=True)if __name__ == '__main__':app.run(port=8979)
Dockerfile 和 requestments.txt
# 使用python的官方镜像作为基础镜像
FROM python:3.6-slim# 设置工作目录
WORKDIR /app# 复制requirements.txt到容器中
COPY requirements.txt .# 安装python依赖
RUN pip install --no-cache-dir -r requirements.txt# 复制项目的所有文件到容器
COPY . .# 暴露端口
EXPOSE 8979# 指定启动命令
CMD ["python", "app.py"]click==8.0.4
colorama==0.4.5
dataclasses==0.8
Flask==2.0.3
importlib-metadata==4.8.3
itsdangerous==2.0.1
Jinja2==3.0.3
MarkupSafe==2.0.1
numpy==1.19.5
opencv-contrib-python==4.6.0.66
Pillow==8.4.0
typing_extensions==4.1.1
Werkzeug==2.0.3
zipp==3.6.0
opencv-python-headless
utils.py
import numpy as np
from PIL import Image, ImageFont, ImageDraw, ImageOpsdef sort_chars(char_list, font, language):if language == "chinese":char_width, char_height = font.getsize("制")elif language == "korean":char_width, char_height = font.getsize("ㅊ")elif language == "japanese":char_width, char_height = font.getsize("あ")elif language in ["english", "german", "french", "spanish", "italian", "portuguese", "polish"]:char_width, char_height = font.getsize("A")elif language == "russian":char_width, char_height = font.getsize("A")num_chars = min(len(char_list), 100)out_width = char_width * len(char_list)out_height = char_heightout_image = Image.new("L", (out_width, out_height), 255)draw = ImageDraw.Draw(out_image)draw.text((0, 0), char_list, fill=0, font=font)cropped_image = ImageOps.invert(out_image).getbbox()out_image = out_image.crop(cropped_image)brightness = [np.mean(np.array(out_image)[:, 10 * i:10 * (i + 1)]) for i in range(len(char_list))]char_list = list(char_list)zipped_lists = zip(brightness, char_list)zipped_lists = sorted(zipped_lists)result = ""counter = 0incremental_step = (zipped_lists[-1][0] - zipped_lists[0][0]) / num_charscurrent_value = zipped_lists[0][0]for value, char in zipped_lists:if value >= current_value:result += charcounter += 1current_value += incremental_stepif counter == num_chars:breakif result[-1] != zipped_lists[-1][1]:result += zipped_lists[-1][1]return resultdef get_data(language, mode):if language == "general":from alphabets import GENERAL as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "english":from alphabets import ENGLISH as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "german":from alphabets import GERMAN as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "french":from alphabets import FRENCH as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "italian":from alphabets import ITALIAN as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "polish":from alphabets import POLISH as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "portuguese":from alphabets import PORTUGUESE as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "spanish":from alphabets import SPANISH as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "A"scale = 2elif language == "russian":from alphabets import RUSSIAN as characterfont = ImageFont.truetype("fonts/DejaVuSansMono-Bold.ttf", size=20)sample_character = "Ш"scale = 2elif language == "chinese":from alphabets import CHINESE as characterfont = ImageFont.truetype("fonts/simsun.ttc", size=10)sample_character = "制"scale = 1elif language == "korean":from alphabets import KOREAN as characterfont = ImageFont.truetype("fonts/arial-unicode.ttf", size=10)sample_character = "ㅊ"scale = 1elif language == "japanese":from alphabets import JAPANESE as characterfont = ImageFont.truetype("fonts/arial-unicode.ttf", size=10)sample_character = "お"scale = 1else:print("Invalid language")return None, None, None, Nonetry:if len(character) > 1:char_list = character[mode]else:char_list = character["standard"]except:print("Invalid mode for {}".format(language))return None, None, None, Noneif language != "general":char_list = sort_chars(char_list, font, language)return char_list, font, sample_character, scale
支持开源:
vietnh1009/ASCII-generator: ASCII generator (image to text, image to image, video to video) (github.com)
相关文章:
python图像彩色数字化
效果展示: 目录结构: alphabets.py GENERAL {"simple": "%#*-:. ","complex": "$B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_~<>i!lI;:,\"^. " } # Full list could be found here…...
cesium 3dtile ClippingPlanes 多边形挖洞ClippingPlaneCollection
原理就是3dtiles里面的属性clippingPlanes 采用ClippingPlaneCollection,构成多边形来挖洞。 其次就是xyz法向量挖洞 clippingPlanes: new this.ffCesium.Cesium.ClippingPlaneCollection({unionClippingRegions: true, // true 表示多个切割面能合并为一个有效的…...
docker 僵尸进程问题
docker僵尸进程 子进程结束后,父进程没有回收该进程资源(父进程可能没有wait),子进程残留资源存放与内核中,就变为僵尸进程(zombie) 场景分析:python脚本A中执行B应用,将A部署在docker中&#…...
微软要求 Windows Insider 用户试用备受争议的召回功能
拥有搭载 Qualcomm Snapdragon 处理器的 Copilot PC 的 Windows Insider 计划参与者现在可以试用 Recall,这是一项臭名昭著的快照拍摄 AI 功能,在今年早些时候推出时受到了很多批评。 Windows 营销高级总监 Melissa Grant 上周表示:“我们听…...
husky,commit规范,生成CHANGELOG.md,npm发版
项目git提交工程化(钩子,提交信息commit message),npm修改版本,需要涉及到的包: husky,允许在git钩子中执行不同的脚步,如commitlint,eslint,prettier&#…...
DETR:一种新颖的端到端目标检测与分割框架
DETR:一种新颖的端到端目标检测与分割框架 摘要: 随着深度学习技术的发展,目标检测和图像分割任务取得了显著的进步。然而,传统的基于区域提名的方法在处理这些问题时存在一定的局限性。为此,Facebook AI Research&am…...
前端js面试知识点思维导图(脑图)
如果看着不清晰可以去https://download.csdn.net/download/m0_73761441/90058523访问下载,无需积分 使用百度脑图制作,可以一键导入下面的文本生成自己的脑图 js相关面试题、知识点 数据类型 1. 数据类型分类?分别包含ÿ…...
【Java基础入门篇】一、变量、数据类型和运算符
Java基础入门篇 一、变量、数据类型和运算符 1.1 变量 计算机中的数据表示方式是:“二进制(0/1)”,但是同时也可以兼容其他进制,例如八进制、十进制、十六进制等。 Java变量的本质是:存储在固定空间的内容,变量名是…...
【llamafactory】安装与环境配置
拉取镜像 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory创建虚拟环境 conda create -n llamafactory python3.10 conda activate llamafactory安装所需依赖 pip install -e ".[torch,vllm,optimum,auto_gptq]"...
Vue 3 + Vuex 埋点实现指南
在现代前端开发中,数据分析和用户行为追踪是不可或缺的部分。本文将介绍如何在 Vue 3 项目中实现埋点功能,具体使用 Vuex 进行状态管理,并通过自定义 Hook 实现埋点逻辑。 目录 项目结构实现埋点逻辑使用埋点功能总结 1.项目结构 我们将创…...
电子应用设计方案-30:智能扫地机器人系统方案设计
智能扫地机器人系统方案设计 一、引言 随着人们生活节奏的加快和对生活品质的追求,智能家居产品越来越受到消费者的青睐。智能扫地机器人作为一种能够自动清扫地面的智能设备,为人们节省了大量的时间和精力。本方案旨在设计一款功能强大、智能化程度高、…...
HTML飞舞的爱心(完整代码)
写在前面 HTML语言实现飞舞的爱心完整代码。 完整代码 <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><title>飞舞爱心</title><style>* {margin: 0;padding: 0;}html,body {overflow: hidd…...
android shader gl_Position是几个分量
在Android的OpenGL ES中,gl_Position是顶点着色器(Vertex Shader)的一个内置输出变量,它用于指定顶点在裁剪空间(Clip Space)中的位置。gl_Position是一个四维向量(4-component vectorÿ…...
spine 动画层 动态权重
前奏.业务背景 这边想实现一个功能,项目中有 一只猫 猫的头会盯着逗猫棒移动。因为素材还没到所以这里使用了 spine 自带的猫头鹰。他的动画 刚好挺有针对性:(关联上篇)https://blog.csdn.net/nicepainkiller/article/details/144…...
《Python基础》之Python中可以转换成json数据类型的数据
目录 一、JSON简介 JSON有两种基本结构 1、对象(Object) 2、数组(Array) 二、将数据装换成json数据类型方法 三、在Python中,以下数据类型可以直接转换为JSON数据类型 1、字典(Dictionary)…...
在oracle下载jdk显示400 Bad Request Request Header Or Cookie Too Large
下载JDK17,官网地址:【https://www.oracle.com/cn/java/technologies/downloads/#jdk17-windows】 问题: 出现 400 Bad Request: Request Header Or Cookie Too Large 错误,通常是由于浏览器存储的 Cookies 或请求头过大所导致的…...
MongoDB注入攻击测试与防御技术深度解析
MongoDB注入攻击测试与防御技术深度解析 随着NoSQL数据库的兴起,MongoDB作为其中的佼佼者,因其灵活的数据模型和强大的查询能力,受到了众多开发者的青睐。然而,与任何技术一样,MongoDB也面临着安全威胁,其…...
【Java基础入门篇】前言
Java基础入门篇 本系列内容主要针对Java基础知识,总共包含四大部分内容: 变量、数据类型和运算符控制语句和递归算法面向对象和JVM底层分析数组和排序 学习需要具备: IDEA编译器 JDK1.8版本 写在前面 在Java入门的最开始,我们需…...
Oracle 建表的存储过程
建表的存储过程 下面是建表的存储过程,用途:通过不同的表,根据不同过滤条件,得到某个字段,例如neid,然后创建一个新表T,表T的表名为拼接XXXX_XXX_neid,表T的字段自行添加 xxx&…...
【Debug】hexo-github令牌认证 Support for password authentication was removed
title: 【Debug】hexo-github令牌认证 date: 2024-07-19 14:40:54 categories: bug解决日记 description: “Support for password authentication was removed on August 13, 2021.” cover: https://pic.imgdb.cn/item/669b38ebd9c307b7e9f3e5e0.jpg 第一章 第一篇博客记录一…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
