在数字世界的无尽探索中,我们时常被那些看似平凡的技术所启发,它们如同星辰般点缀着我们的创意天空。今天,我突发奇想,想要用Python开发一个将图片转化为字符画的小工具。这不仅是一次技术的实践,更是一场艺术与科技的奇妙融合。
让我们一起踏上这段旅程,用Python的代码,绘制出属于我们自己的字符画世界。在这个世界里,每一行代码都是一笔,每一次运行都是一次创作,每一张字符画都是一次心灵的触动。
1.功能概述
使用PyQt5框架开发的ASCII字符画生成器。用户可以通过该程序加载图片,将其转换为ASCII字符表示,并保存生成的ASCII字符画。
效果图
2.设计思路
- 用户界面设计:使用PyQt5创建一个图形用户界面,包括图片显示区域、文本编辑区域、ASCII字符设置区域和操作按钮。
- 图片处理:加载图片后,调整图片大小并转换为灰度图像,以便于生成ASCII艺术。
- ASCII艺术生成:根据灰度图像的像素值,将每个像素映射到预设的ASCII字符集中的一个字符。
- 保存ASCII艺术:将生成的ASCII字符串保存为文本文件。
3.主要函数解析
__init__
:初始化主窗口,设置UI元素。initUI
:设置窗口标题和布局,添加必要的控件和按钮。load_image
:打开文件对话框,加载用户选择的图片。resize_image
:调整图片大小,保持宽高比。grayify
:将图片转换为灰度图像。generate_ascii
:生成ASCII艺术,将灰度图像转换为ASCII字符串,并在文本编辑器中显示。save_ascii
:保存生成的ASCII艺术到文本文件。pixels_to_ascii
:将灰度图像的像素转换为ASCII字符。
4.详细设计
-
界面设计:
- 左侧区域用于显示原始图片。
- 右侧上半部分用于显示生成的ASCII字符。
- 右侧下半部分包括:
- 自定义ASCII字符输入框。
- 加载图片、生成ASCII、保存ASCII的按钮。
-
功能实现:
- 加载图片:用户点击按钮后,通过文件对话框选择图片文件,加载并显示在左侧区域。
- 生成ASCII:点击按钮后,程序将图片转换为灰度并调整大小,然后根据灰度值映射到ASCII字符,结果显示在右侧文本编辑区域。
- 保存ASCII:用户点击按钮后,选择保存位置和文件名,将文本编辑区域的内容保存为文本文件。
-
错误处理:
- 在加载图片和生成ASCII时,程序会检查是否有图片路径,如果没有则不执行后续操作。
- 在生成ASCII时,如果自定义的ASCII字符集为空,程序会提示用户输入。
-
用户体验:
- 提供清晰的界面布局和直观的操作流程。
- 在执行可能耗时的操作(如生成ASCII)时,提供进度反馈或加载指示。
5.完整代码
# -*- coding: gbk -*-
"""
Created on 2024/6/18 11:22
@Deprecated:
@Author: DanMo
@File : ASCIIArtGenerator.py
"""
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QTextEdit, QFileDialog, QVBoxLayout, \
QHBoxLayout, QWidget, QLineEdit
from PyQt5.QtGui import QPixmap
from PIL import Image
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ASCII_CHARS = '@%#*+=-:. '
self.initUI()
def initUI(self):
self.setWindowTitle('ASCII Art Generator')
central_widget = QWidget(self)
self.setCentralWidget(central_widget)
# 创建布局
main_layout = QHBoxLayout(central_widget)
# 左侧显示图片的部分
self.label = QLabel(self)
main_layout.addWidget(self.label)
# 右侧操作部分
right_layout = QVBoxLayout()
self.textEdit = QTextEdit(self)
right_layout.addWidget(self.textEdit)
# 添加设置ASCII字符的部分
ascii_label = QLabel('Custom ASCII Chars:', self)
right_layout.addWidget(ascii_label)
self.ascii_edit = QLineEdit(self)
self.ascii_edit.setText(self.ASCII_CHARS)
right_layout.addWidget(self.ascii_edit)
button_layout = QHBoxLayout()
self.btnLoad = QPushButton('Load Image', self)
self.btnLoad.clicked.connect(self.load_image)
button_layout.addWidget(self.btnLoad)
self.btnGenerate = QPushButton('Generate ASCII', self)
self.btnGenerate.clicked.connect(self.generate_ascii)
button_layout.addWidget(self.btnGenerate)
self.btnSave = QPushButton('Save ASCII', self)
self.btnSave.clicked.connect(self.save_ascii)
button_layout.addWidget(self.btnSave)
right_layout.addLayout(button_layout)
main_layout.addLayout(right_layout)
self.resize(800, 600) # 初始窗口大小
def load_image(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Open Image File", "", "Image Files (*.png *.jpg *.jpeg *.bmp)",
options=options)
if fileName:
pixmap = QPixmap(fileName)
self.label.setPixmap(pixmap)
self.image_path = fileName
# 调整图片尺寸并灰度化
def resize_image(self, image, new_width=100):
width, height = image.size
ratio = height / width / 2
new_height = int(new_width * ratio)
resized_image = image.resize((new_width, new_height))
return resized_image
def grayify(self, image):
return image.convert('L')
def generate_ascii(self):
if not hasattr(self, 'image_path'):
return
try:
image = Image.open(self.image_path)
except Exception as e:
print(e)
return
image = self.resize_image(image, new_width=100)
image = self.grayify(image)
self.ASCII_CHARS = self.ascii_edit.text()
if not self.ASCII_CHARS:
print('Please input ASCII_CHARS!')
return
ascii_str = self.pixels_to_ascii(image)
img_width = image.width
ascii_img = ''
for i in range(0, len(ascii_str), img_width):
ascii_img += ascii_str[i:i + img_width] + '\n'
self.textEdit.setPlainText(ascii_img)
def save_ascii(self):
if not hasattr(self, 'image_path'):
return
try:
image = Image.open(self.image_path)
except Exception as e:
print(e)
return
image = self.resize_image(image, new_width=100)
image = self.grayify(image)
self.ASCII_CHARS = self.ascii_edit.text()
if not self.ASCII_CHARS:
print('Please input ASCII_CHARS!')
return
ascii_str = self.pixels_to_ascii(image)
img_width = image.width
ascii_img = ''
for i in range(0, len(ascii_str), img_width):
ascii_img += ascii_str[i:i + img_width] + '\n'
options = QFileDialog.Options()
fileName, _ = QFileDialog.getSaveFileName(self, "Save ASCII File", "", "Text Files (*.txt)", options=options)
if fileName:
with open(fileName, 'w') as f:
f.write(ascii_img)
def pixels_to_ascii(self, image):
pixels = image.getdata()
ascii_str = ''
for pixel in pixels:
# 确保灰度值在合理范围内
pixel_char = self.ASCII_CHARS[min(len(self.ASCII_CHARS) - 1, pixel // 25)]
ascii_str += pixel_char
return ascii_str
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
6.效果展示
自定义设置字符
另存为文本