如何用代码画出一幅好看的画4 A# B w }# X: |
& Y3 I! b% y+ G% ]* }# _0 R; g1 D
前言* U; a( \* h$ \/ }* h/ T8 ^
随着计算机技术的逐步发展,借助计算机强大的能力进行绘画操作,已经变得非常容易。绘画作品的水平也是越来越高,下面就来看看用编程语言绘画,能搞出什么有意思的东西,相比人类的创作又有哪些值得我们思考的地方。 7 M1 V0 L9 U7 p- p& f" V+ N' ]+ {8 V9 Z! O! v5 m; B- A& h, i
绘画 % b" ]/ A2 s) G1 O用代码输出类似绘画的内容,最最原始的恐怕就是直接使用 print 函数了。/ P7 n0 A/ ?. d' F" X
8 ^& M1 i0 V, K$ Kprint 输出字符- {. f. X& p! z$ w
def do_print(): 4 s1 J; D' }, Y5 R9 ~% p" s3 p) a print(''' 6 c* E; Q* Q' Z: g# ]4 a' U/ b % y' n) T9 B" M3 f+ G へ /| p$ }0 {7 ?' l0 M9 T% j& v: x
/\7 ∠_/ 1 v. H$ k) F } / │ / / ' |8 [- `3 X3 a s │ Z _,< / /`ヽ 4 J* e) r) l8 t' m0 Y% y+ C │ ヽ / 〉 " `8 {# |4 e! K/ b6 ~2 H. T0 V6 ~ Y ` / / 1 H) C- X" `2 U9 J% I: ?: w イ● 、 ● ⊂⊃〈 /* W6 D7 _4 L9 ]/ L5 \
() へ | \〈; Y w, D3 Y/ m$ d; D' O1 ?
>ー 、_ ィ │ // ) I( y- O+ m5 p( y5 F / へ / ノ<| \\ 8 ]; R# A2 V6 b" W: n( } ヽ_ノ (_/ │//) x& X% u4 l& e8 ~ H/ ~/ P
7 |/ ( k0 s' }. U- |; J7 T$ ]8 c >―r ̄ ̄`ー―_ ! ~5 q7 J, ]) [$ d. D% M ''')+ T( }: J8 W4 L& c5 `8 B, P
1' [6 A4 [) a' m' b% Q# e% y
2 ) L% g- _5 f0 F4 R; R: ~" _3 7 \- ]% J: r6 n# R9 y$ O4+ s9 ?& M( n# {; T* K E/ A2 i
5% Y3 y3 w( h l( M
6 6 Y! m% c0 f: ?: b% O) N9 L9 b- V7& o; o' G0 U/ u( L
8 : u2 i6 R* x/ [3 ]. A/ k9 u9- s1 W) o' \, ^( r! V9 h' e
109 b( m- l: H1 K4 k
11! V. ^$ g6 ~) I) L* u0 G- ?' e4 k. ~
12. }- _$ g# T/ f- J B
13 5 Q* C5 U0 @7 g: X' ]( ^5 Q14# i" o0 a! `$ _3 C( M0 W1 i9 |
15 ' ~' G% Q, ^* A$ `16 % r' ?9 j$ p* b17! D' _$ o( O- m. z6 G
这个方法执行后,就可以在控制台看到下面这幅可爱的皮卡丘画了。 & h& ?5 @7 y6 Y8 N( {3 N& M8 I. F$ I4 N- r/ p8 X# }
% O5 x6 q w. o0 A; v) v. v% k
$ x1 E3 E' K# S6 q
这种做法,可以说是完全没有什么技术水平了。想要画什么内容,还得预先编排字符,太麻烦了。于是便有了升级版。7 k, C4 J# \* R. i% A' m
2 c5 d' i/ T ]# o. m I图片转字符. j0 _' ^( g% F
这种用图片转字符,看似没有什么意义,但其实有时候可以创作出意想不到的作品。比如前一阵子的顶流冰墩墩。( N( f+ a" D) e0 l% q
/ n- J: o6 V8 q' g 1 @* d& e' z F |# F) G7 M: P. j5 u, G% s" d
这种做法的思路总的来说,就是把图片的抽样图片的像素信息,然后将像素信息中关键的亮度信息用特殊的字符串进行打印,效果就是我们最终看到的样子。+ p p* E8 c. l; i6 e, H& B
+ c/ P4 f* \: V! i" G, }& hpublic static void printImage(BufferedImage image) throws IOException { 3 H/ x: \( Q3 q final char[] PIXEL_CHAR_ARRAY = {'W', '@', '#', '8', '&', '*', 'o', ':', '.', ' '}; , t" I* L3 j. E( e1 ] ] int width = image.getWidth();: I( ~( J# T7 v4 _% j) S
int height = image.getHeight();6 b4 Y, @- c* o7 o7 _
for (int i = 0; i < height; i++) {7 P0 o$ v. y8 r4 g- J
for (int j = 0; j < width; j++) { : @7 S, I/ P5 H" @3 t A4 X int rgb = image.getRGB(j, i);. ~) T6 M& W" }2 a8 C$ N
Color color = new Color(rgb); % W4 _$ V: s! z. M2 J: K+ ? int red = color.getRed(); 8 u' N/ l. j$ P+ b" t @ int green = color.getGreen();3 O6 A) @" B1 V( ]4 U' A% P
int blue = color.getBlue(); 5 A( q3 ~5 e! l) p! o // 一个用于计算RGB像素点灰度的公式 9 d$ A: G$ I1 b Double grayscale = 0.2126 * red + 0.7152 * green + 0.0722 * blue; " u) s/ H; |$ @ double index = grayscale / (Math.ceil(255 / PIXEL_CHAR_ARRAY.length) + 0.5); 0 w% a8 P/ \- [ System.out.print(PIXEL_CHAR_ARRAY[(int)(Math.floor(index))]); $ h, n& H# z' _* J! x9 c9 U } c5 x3 T. ?8 f7 ~5 A3 J
System.out.println(); ) v2 n$ E* p4 }+ h }- }8 G$ {+ x V
} 2 s8 e9 C% t) f 3 M" U0 q$ _. n$ x( W1% Z7 K8 f; X0 y" y
25 J: r2 t1 E! c3 V4 u" L
38 y8 H6 C! \4 V y# j
4 4 M8 z4 [% g# d' `51 {+ }% n3 ^+ K2 y) e3 h; n
6/ [0 ^* L2 O2 S, Z5 y0 g) ]
7 $ _- @. P n# |8$ J1 K2 Q' w( r" x
9 , T% q# S8 Q, j9 I9 I8 l10 ) H& r; O9 I0 @11 : K* Y u; Q5 I" B' @1 }12* p" I% e! @; G! A3 u" h0 c; _: |
13* t# S3 I& \5 _% B& M
14 2 x4 N3 y4 T) U" X& V15$ d" `8 @8 x1 l6 ~* M
16 6 P% V1 J/ d1 [6 M179 n2 i0 E; U8 v/ R4 ?
18 3 ^6 d/ K- X' @6 i8 |$ j6 ]/ @19; e, F) w5 L% ]9 o; X! s& @. H
更多具体实现可以参考 字符作画,我用字符画个冰墩墩 。 ' W7 T/ g8 ?; \/ P$ W5 J: y ! F1 [5 q( E. G9 T. x) N; b使用 Python-turtle 库作图- u7 w. ?" _# @; f- ~9 A
下面这几幅知乎用户的回答,可以说是非常有水平了。: k3 `3 H* x9 s& h$ {
% h% L+ |1 r. Y j
: t' D/ g3 o3 }. _) t# N' U
! {3 W1 W _1 H2 ^( }
import random9 }# e/ P/ H# K2 ~0 y
import turtle ! d3 i" E5 a; }: l- m: ?from turtle import * : m0 \- k4 K5 r0 ] , I$ e3 s$ } K! ut = turtle.Turtle() : {; P) e1 {: A3 E0 E% qw = turtle.Screen() ; |4 ~8 @" i$ O8 a" P2 l/ Y" U% ^ [' X9 h1 K& E2 W5 a Z, Y5 s 8 e* T. ~$ \0 x1 r$ s u pdef tree(branchLen, t):0 ]4 T9 C9 f' x0 k( L9 y4 \- v
if branchLen > 3:9 ]# X) Q! H1 u1 ?
if 8 <= branchLen <= 12:7 h) A9 `( F0 Z1 p" X
if random.randint(0, 2) == 0: c# k% k/ i4 O) K
t.color('snow')7 o. T/ j, r2 L6 X
else: 9 n5 R2 t: t" t0 o7 d' P; A t.color('lightcoral')# R" `- P# `/ g5 }$ n/ y
t.pensize(branchLen / 3)- k4 ]6 C% U6 u& ~" p' u: H
elif branchLen < 8:; v" t7 N* _3 a
if random.randint(0, 1) == 0:( P4 v4 f, P1 R
t.color('snow') - D4 z* ^; s2 A# I3 @( N else:3 G* d* Y% N. V' l2 `" ]2 Z$ \2 E
t.color('lightcoral'), a- G% V5 T4 q2 \7 V7 ?
t.pensize(branchLen / 2)# n: P! j% D0 H, O% @9 ~4 x
else: . U Q6 i4 E# C" n" ?8 v& g* {0 Y/ E t.color('sienna') & G6 f: a& w# { t.pensize(branchLen / 10) * t. \6 \0 w( J - P4 ]& D- n8 b" a: c t.forward(branchLen) b. ]( |/ A; u2 p8 ]. ? a = 1.5 * random.random()0 b/ Q5 M# _3 z, x0 v
t.right(20 * a) ; W. ?. E8 S" m4 z( v5 y b = 1.5 * random.random() ' F, }# {* o- h: h0 R# d tree(branchLen - 10 * b, t)6 w4 K. m. d- |" ?$ t) b: N
t.left(40 * a) }* I, G" \% O8 o5 W) F% @5 o
tree(branchLen - 10 * b, t)2 J [* ]7 r: K2 o: G' J, x I9 Q
t.right(20 * a) 0 k# c" X: H3 m( z, p K t.up()& \" \7 q- A* W8 [9 p7 o0 {8 ?
t.backward(branchLen) . d0 {, e; u& y+ N8 N t.down()+ {; D7 s& b0 a4 d6 v4 H2 L
/ l s: Y3 O( m" P
: L: a, B8 C, `, h9 y9 G8 idef petal(m, t): # 树下花瓣% H% I+ o. G. l1 _4 P' f
for i in range(m):& L9 L6 q6 m1 {5 G) s
a = 200 - 400 * random.random() - s* b. u+ F7 }& C# X( ]7 N0 q( ] b = 10 - 20 * random.random() " Q5 L& `3 `6 P8 l: { t.up() 6 u& y5 U; k' |0 r% [$ W E: z5 T$ e t.forward(b)3 d) X+ ^+ M/ W; ~. Q' t; E. g
t.left(90) / g5 M+ N, @; ~: T7 ^7 h t.forward(a)9 E: A/ q# i6 s/ R4 Z- g) }) j8 v
t.down()& H+ I9 s/ M# _! m) @6 w
t.color("lightcoral") 4 I; b6 {2 m* {; P3 Z t.circle(1) 3 e' L2 m- K! ^) o t.up() ! j: ~& i+ ^# ^: A5 K- k* p# P t.backward(a) & U; T, `" E7 p7 s7 m7 ] t.right(90)8 b% L4 t9 M* B$ G$ l- E! U
t.backward(b)3 u: y, l8 J/ g3 e& m
6 q4 b' J* M: ~' v $ Y# R# s. }8 f1 }% G, kdef main(): 4 r" M6 D( j/ b6 g2 F2 w% T t = turtle.Turtle() & a& K* d$ O0 q g; N myWin = turtle.Screen() T( n' o3 B5 U
getscreen().tracer(5, 0) 0 {1 j5 y3 Q& T5 }* L turtle.screensize(bg='wheat') 3 \# |7 l( k: M8 S6 ? t.left(90) 0 r' G: D- ^8 R2 a7 g t.up() : P* a1 e0 I. J8 t; ^ t.backward(150)0 I) y+ ~+ z$ ~ u5 k* d
t.down()$ _; p2 Q/ H( y6 I5 W0 ^( K
t.color('sienna') % S5 V) w) _5 h tree(60, t)) B% f: | s, \3 k' `
petal(100, t) 6 V* y) }5 }0 Z0 k) } S u' o! d3 a: K5 f( a2 {/ W
myWin.exitonclick() ( G# J8 B0 a! B8 T) e1 Q" C; r8 Z/ f$ w
/ r: B5 z. y8 {# fif __name__ == '__main__':, G4 T+ m: L" H/ Z8 k0 M/ Y
main(), O' B8 ^+ |* C0 M: P% I
7 D' W9 z: P6 b8 G+ o6 J
1 X N, N! ?& v; ?/ Z
1+ k( ~1 l- d( Q' d+ q
21 X& v( @2 }+ t" ?3 J
3 2 f+ V& Z) B; K/ P4% e. t8 h- J) Z/ p T4 u, n
5; {$ s! [' c1 g2 U
6 2 X& B0 V8 E( L7: j& E7 \% q b% T% R% {
8) f. ?9 a3 g7 f7 v* F
9( @, }7 R! I, L: R/ H' d
10 & r% E& l* k7 @# p" F H; f11 + h5 N* y3 d1 l- l1 o! z12 / s& V9 }* t. Q133 v6 `3 s* D9 a7 ]1 f6 _; {+ g
14 * ~' j2 R* a0 x# _1 {15 " I% R* H" h6 a9 k16; d/ j7 P' h' @3 H
17 4 Y7 g: b$ Y' g: U5 W4 ~$ o/ i18# q% ]8 C) v$ k. C% T
19 t- V; x& S7 s- N' W: b# O6 r20$ O7 {9 r# a, Z" q5 n8 u e1 G- _
21' G3 d4 j0 |% X$ @- q; x2 ^( D
22* V* A2 o5 S/ O4 I# P
23 ; a' X% t7 N* Y! F; l24 + w# c8 v4 S# x( B) k/ o25 ) \; z" ?& C9 l% E/ a5 m5 c3 A26 ! q2 X$ h# v! s- K9 s6 b. p% D# O* Y27 9 s6 Y. J; o6 k9 Q. d1 K8 X28( ? p) S: C; W+ @+ h. m
29 0 B! |' E0 c6 u: Q0 f( U30 ) Q3 P! g, L. }: b( ] X31( m7 D* S( c/ O. N8 Z
32 - o% i1 i! N/ x/ c7 L33 7 K- t; d1 h. O8 |" }34 " d0 p C. x! E9 D35& g: | `; Q" J6 d2 _1 K1 l
36 & C* ^4 L5 h' j$ p9 t4 T% \+ c8 y% W37 g$ @. d5 _. C8 ]$ H38 ) g3 z! s! r1 u! i% b39 ( v& m! f$ \' H7 s2 A) C3 t40. e3 p' F8 q$ r2 }
41% U5 ?9 A* Y' b
42 7 M# C9 U$ W& U X43 / j* U4 w, ^0 J$ J) b5 N7 U44/ p8 ?$ b1 N$ S" w
45 , ^+ p) y: q1 r% `2 h4 \9 L460 M2 l/ w$ O8 l3 m, v
47 ' V1 k3 G) @2 c6 X6 v48, N( k$ X) {8 y1 `- }3 y H& [
49 9 M4 b& s* J/ S) y# z* N2 V# i50 ' {3 g, G& S1 f1 s51 3 b: I6 E: ?8 O9 f52 ! N0 K7 t1 b& u7 e& G/ v: @53 - H D, O( ^' `( _# m54 3 ]: t9 w4 g. G3 R: w. d7 |% x8 v55" D8 o# _' u& \. M
564 ]" H, {# i- S' h7 M
57 9 H, Y" I; [6 J* i0 l) B) B58 $ O7 K8 ~! b! g6 D, L6 C59) Y: I( g- d1 ~' k
600 ~ W) T6 E5 |4 p+ i
615 Q3 z: e6 y3 @ V6 x+ ~* K! Y9 _4 P
62 & J8 g" z9 }0 p( q0 H3 d2 Y* Q633 v. S+ R `# @0 P t
64 ; x8 b, ]- H9 R' T' g653 c3 i( E$ B' g% I9 _( ?
665 K7 _! q( I7 p% t
67 " O2 s! @$ |" J- h' ]& `68! Z* F; O# f: w. V1 s
69 0 F9 o j7 M( g9 q* S70" Y% i( q# ?' p
71 5 W9 v1 X* q+ B+ I f3 F. p72# z3 I8 N5 I& ^: K7 y% k- `
73* N0 i2 o5 w% d) d' O
74 ' a. B. ?% Z5 k" o; ]; {75 9 B+ z) y/ n# m# r k* U$ n; S& F0 I
! V; c- Z! ]) X/ j9 Z- [* L
' ?1 r3 y/ p* K' V. ] Q8 Q
可以看到,使用 turtle 库加上强大的数学几何知识,可以创作出非常好的看图像。更多内容可以参考 这个问题 - O5 ]2 Z0 l( w% l) M8 z Y8 {( ?7 X; K3 H
使用 AI 作画 , i9 ^6 v& z: n/ m+ j: q. v9 CERNIE-ViLG文生图) v1 T1 E, ~ K, l+ Y- b! N! I/ Y
使用以上方案绘制的内容,一般都比较刻板,缺少一些人文气息。少了一些人类才能有的特点。但是随着 AI 算力的提升和大数据的逐步发展,已经有很多可以使用 AI 进行作画的平台了。 比如 百度飞浆文心大模型 ERNIE-ViLG文生图 平台。 ' C3 u+ V/ j5 j3 w) K3 \: e3 n 7 o8 D& K% v- e, q( }9 c" a5 limport wenxin_api7 K* M1 C* M3 x* e0 Y+ h
from wenxin_api.tasks.text_to_image import TextToImage% z: J8 s1 j+ w5 }, Y9 N
import secret3 m6 T/ V, D1 w/ t5 U u
( _5 }9 O% b: v u' R 6 U& h8 c7 r0 \3 [def draw(t, s): : v K7 n; n0 l" O& n, ? wenxin_api.ak = secret.ak $ O# S& W' P l7 o% {; y3 ^! w wenxin_api.sk = secret.sk. z$ P& q/ R# F! u9 B; c1 e
input_dict = { {' B/ N& P/ k: d5 U
"text": t, 1 c1 U+ ]" M. @" l. {- \ "style": s 8 p, V, Z/ }& _! _4 J) d } : z# e. @% s9 e' A9 r3 { rst = TextToImage.create(**input_dict)* F$ p3 K. O, \* D
print(rst)8 r& h5 y) X3 q# q8 j8 x+ @