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 第一章 第一篇博客记录一…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...