说明

  本文发布较早,查看最新动态,请关注 GitHub 项目。(2020 年 1 月 注)

准备

  全新的图形引擎与 AI 算法,高效流畅地绘出任何一副美丽的图像。

  IDE:VisualStudio

  Language:VB.NET / C#

  Graphics:EDGameEngine

第一节 背景

  背景是图画里衬托主体事物的景象。

图1-1 先画个蓝蓝的天空

  蓝天、白云和大地,程序最擅长这种色调单一的涂抹了。

第二节 轮廓

  轮廓是物体的外周或图形的外框。

图2-2 勾勒人物和衣饰轮廓

  现在 AI 要控制笔触大小和颜色,让图像的主体显现出来。

第三节 光影

  光影是物体在光的照射下呈现出明与暗的关系。

图3-1 光影提升画面质感

  AI 可不懂什么是光影,在上一步的基础上优化细节即可。

第四节 润色

  润色是增加物体本身及其周围的色彩。

图4-1 画面润色

  这是关键一步,AI需要将丢失的颜色细节补缺回来。

第五节 成型

  大功告成!前面所有的步骤都是为这一步铺垫。

图5-1 人物已经栩栩如生啦

  事实上 AI 只进行这一步也可以画出完整的图像,但没有过渡会显得生硬。

第六节 算法

  算法思路很简单,计算画笔轨迹后一遍遍重绘,感觉上是人类画手的效果。 

  不再是二值化

  因为现在要绘制全彩图像,将图像划分为只有黑和白的效果已经没有什么意义,二值化不再适用

  适用的方法是将 RGB 颜色空间划分为若干个颜色子空间,然后逐个处理一幅图像中属于某个子空间的区域

  自动循迹

  循迹算法没有大的变动,仍是早前博客里贴出的代码

  彩色图像线条较短,可以不再计算点周围的权值用来中断轨迹

  重绘

  程序先选择笔触较大、颜色淡的画笔绘制一遍,然后在这基础上逐步减小笔触并加深色彩

  直接按照标准笔触可以一遍成型,但会显得突兀和生硬,毕竟这个AI不是真的在思考如何画一幅图像

Imports System.Numerics
''' <summary>
''' 表示自动循迹并生成绘制序列的AI
''' </summary>
Public Class SequenceAI
''' <summary>
''' 线条序列List
''' </summary>
''' <returns></returns>
Public Property Sequences As List(Of PointSequence)
''' <summary>
''' 扫描方式
''' </summary>
Public Property ScanMode As ScanMode = ScanMode.Rect
Dim xArray() As Integer = {-, , , , , , -, -}
Dim yArray() As Integer = {-, -, -, , , , , }
Dim NewStart As Boolean
''' <summary>
''' 创建并初始化一个可自动生成绘制序列AI的实例
''' </summary>
Public Sub New(BolArr(,) As Integer)
Sequences = New List(Of PointSequence)
CalculateSequence(BolArr)
For Each SubItem In Sequences
SubItem.CalcSize()
Next
End Sub
''' <summary>
''' 新增一个序列
''' </summary>
Private Sub CreateNewSequence()
Sequences.Add(New PointSequence)
End Sub
''' <summary>
''' 在序列List末尾项新增一个点
''' </summary>
Private Sub AddPoint(point As Vector2)
Sequences.Last.Points.Add(point)
End Sub
''' <summary>
''' 计算序列
''' </summary>
Private Sub CalculateSequence(BolArr(,) As Integer)
If ScanMode = ScanMode.Rect Then
ScanRect(BolArr)
Else
ScanCircle(BolArr)
End If
End Sub
''' <summary>
''' 圆形扫描
''' </summary>
''' <param name="BolArr"></param>
Private Sub ScanCircle(BolArr(,) As Integer)
Dim xCount As Integer = BolArr.GetUpperBound()
Dim yCount As Integer = BolArr.GetUpperBound()
Dim CP As New Point(xCount / , yCount / )
Dim R As Integer =
For R = To If(xCount > yCount, xCount, yCount)
For Theat = To Math.PI * Step / R
Dim dx As Integer = CInt(CP.X + R * Math.Cos(Theat))
Dim dy As Integer = CInt(CP.Y + R * Math.Sin(Theat))
If Not (dx > And dy > And dx < xCount And dy < yCount) Then Continue For
If BolArr(dx, dy) = Then
BolArr(dx, dy) =
Me.CreateNewSequence()
Me.AddPoint(New Vector2(dx, dy))
CheckMove(BolArr, dx, dy, )
NewStart = True
End If
Next
Next
End Sub
''' <summary>
''' 矩形扫描
''' </summary>
''' <param name="BolArr"></param>
Private Sub ScanRect(BolArr(,) As Integer)
Dim xCount As Integer = BolArr.GetUpperBound()
Dim yCount As Integer = BolArr.GetUpperBound()
For i = To xCount -
For j = To yCount -
Dim dx As Integer = i
Dim dy As Integer = j
If Not (dx > And dy > And dx < xCount And dy < yCount) Then Continue For
If BolArr(dx, dy) = Then
BolArr(dx, dy) =
Me.CreateNewSequence()
Me.AddPoint(New Vector2(dx, dy))
CheckMove(BolArr, dx, dy, )
NewStart = True
End If
Next
Next
End Sub
''' <summary>
''' 递归循迹算法
''' </summary>
Private Sub CheckMove(ByRef bolArr(,) As Integer, ByVal x As Integer, ByVal y As Integer, ByVal StepNum As Integer)
If StepNum > Then Return
Dim xBound As Integer = bolArr.GetUpperBound()
Dim yBound As Integer = bolArr.GetUpperBound()
Dim dx, dy As Integer
Dim AroundValue As Integer = GetAroundValue(bolArr, x, y)
'根据点权值轨迹将在当前点断开
'If AroundValue > 2 AndAlso AroundValue < 8 Then
'Return
'End If
For i = To
dx = x + xArray(i)
dy = y + yArray(i)
If Not (dx > And dy > And dx < xBound And dy < yBound) Then
Return
ElseIf bolArr(dx, dy) = Then
bolArr(dx, dy) =
If NewStart = True Then
Me.CreateNewSequence()
Me.AddPoint(New Vector2(dx, dy))
NewStart = False
Else
Me.AddPoint(New Vector2(dx, dy))
End If
CheckMove(bolArr, dx, dy, StepNum + )
NewStart = True
End If
Next
End Sub
''' <summary>
''' 返回点权值
''' </summary>
Private Function GetAroundValue(ByRef BolArr(,) As Integer, ByVal x As Integer, ByVal y As Integer) As Integer
Dim dx, dy, ResultValue As Integer
Dim xBound As Integer = BolArr.GetUpperBound()
Dim yBound As Integer = BolArr.GetUpperBound()
For i = To
dx = x + xArray(i)
dy = y + yArray(i)
If dx > And dy > And dx < xBound And dy < yBound Then
If BolArr(dx, dy) = Then
ResultValue +=
End If
End If
Next
Return ResultValue
End Function
End Class ''' <summary>
''' 线条扫描方式
''' </summary>
Public Enum ScanMode
''' <summary>
''' 矩形扫描
''' </summary>
Rect
''' <summary>
''' 圆形扫描
''' </summary>
Circle
End Enum

VB.NET-SequenceAI

Imports System.Numerics
''' <summary>
''' 表示由一系列点向量组成的线条
''' </summary>
Public Class PointSequence
Public Property Points As New List(Of Vector2)
Public Property Sizes As Single()
''' <summary>
''' 计算画笔大小
''' </summary>
Public Sub CalcSize()
If Points.Count < Then Exit Sub
Static Mid, PenSize As Single
ReDim Sizes(Points.Count - )
For i = To Points.Count -
Mid = CSng(Math.Abs(i - Points.Count / ))
PenSize = - Mid / Points.Count *
Sizes(i) = PenSize
Next
End Sub
End Class

VB.NET-PointSequence

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Numerics;
/// <summary>
/// 表示自动循迹并生成绘制序列的AI
/// </summary>
public class SequenceAI
{
/// <summary>
/// 线条序列List
/// </summary>
/// <returns></returns>
public List<PointSequence> Sequences { get; set; }
/// <summary>
/// 扫描方式
/// </summary>
public ScanMode ScanMode { get; set; }
int[] xArray = {
-,
,
,
,
,
,
-,
-
};
int[] yArray = {
-,
-,
-,
,
,
,
, };
bool NewStart;
/// <summary>
/// 创建并初始化一个可自动生成绘制序列AI的实例
/// </summary>
public SequenceAI(int[,] BolArr)
{
Sequences = new List<PointSequence>();
CalculateSequence(BolArr);
foreach (object SubItem_loopVariable in Sequences) {
SubItem = SubItem_loopVariable;
SubItem.CalcSize();
}
}
/// <summary>
/// 新增一个序列
/// </summary>
private void CreateNewSequence()
{
Sequences.Add(new PointSequence());
}
/// <summary>
/// 在序列List末尾项新增一个点
/// </summary>
private void AddPoint(Vector2 point)
{
Sequences.Last.Points.Add(point);
}
/// <summary>
/// 计算序列
/// </summary>
private void CalculateSequence(int[,] BolArr)
{
if (ScanMode == ScanMode.Rect) {
ScanRect(BolArr);
} else {
ScanCircle(BolArr);
}
}
/// <summary>
/// 圆形扫描
/// </summary>
/// <param name="BolArr"></param>
private void ScanCircle(int[,] BolArr)
{
int xCount = BolArr.GetUpperBound();
int yCount = BolArr.GetUpperBound();
Point CP = new Point(xCount / , yCount / );
int R = ;
for (R = ; R <= xCount > yCount ? xCount : yCount; R++) {
for (Theat = ; Theat <= Math.PI * ; Theat += / R) {
int dx = Convert.ToInt32(CP.X + R * Math.Cos(Theat));
int dy = Convert.ToInt32(CP.Y + R * Math.Sin(Theat));
if (!(dx > & dy > & dx < xCount & dy < yCount))
continue;
if (BolArr[dx, dy] == ) {
BolArr[dx, dy] = ;
this.CreateNewSequence();
this.AddPoint(new Vector2(dx, dy));
CheckMove(ref BolArr, dx, dy, );
NewStart = true;
}
}
}
}
/// <summary>
/// 矩形扫描
/// </summary>
/// <param name="BolArr"></param>
private void ScanRect(int[,] BolArr)
{
int xCount = BolArr.GetUpperBound();
int yCount = BolArr.GetUpperBound();
for (i = ; i <= xCount - ; i++) {
for (j = ; j <= yCount - ; j++) {
int dx = i;
int dy = j;
if (!(dx > & dy > & dx < xCount & dy < yCount))
continue;
if (BolArr[dx, dy] == ) {
BolArr[dx, dy] = ;
this.CreateNewSequence();
this.AddPoint(new Vector2(dx, dy));
CheckMove(ref BolArr, dx, dy, );
NewStart = true;
}
}
}
}
/// <summary>
/// 递归循迹算法
/// </summary>
private void CheckMove(ref int[,] bolArr, int x, int y, int StepNum)
{
if (StepNum > )
return;
int xBound = bolArr.GetUpperBound();
int yBound = bolArr.GetUpperBound();
int dx = ;
int dy = ;
int AroundValue = GetAroundValue(ref bolArr, x, y);
//根据点权值轨迹将在当前点断开
//If AroundValue > 2 AndAlso AroundValue < 8 Then
//Return
//End If
for (i = ; i <= ; i++) {
dx = x + xArray[i];
dy = y + yArray[i];
if (!(dx > & dy > & dx < xBound & dy < yBound)) {
return;
} else if (bolArr[dx, dy] == ) {
bolArr[dx, dy] = ;
if (NewStart == true) {
this.CreateNewSequence();
this.AddPoint(new Vector2(dx, dy));
NewStart = false;
} else {
this.AddPoint(new Vector2(dx, dy));
}
CheckMove(ref bolArr, dx, dy, StepNum + );
NewStart = true;
}
}
}
/// <summary>
/// 返回点权值
/// </summary>
private int GetAroundValue(ref int[,] BolArr, int x, int y)
{
int dx = ;
int dy = ;
int ResultValue = ;
int xBound = BolArr.GetUpperBound();
int yBound = BolArr.GetUpperBound();
for (i = ; i <= ; i++) {
dx = x + xArray[i];
dy = y + yArray[i];
if (dx > & dy > & dx < xBound & dy < yBound) {
if (BolArr[dx, dy] == ) {
ResultValue += ;
}
}
}
return ResultValue;
}
} /// <summary>
/// 线条扫描方式
/// </summary>
public enum ScanMode
{
/// <summary>
/// 矩形扫描
/// </summary>
Rect,
/// <summary>
/// 圆形扫描
/// </summary>
Circle
}

C#-SequenceAI

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Numerics;
/// <summary>
/// 表示由一系列点向量组成的线条
/// </summary>
public class PointSequence
{
public List<Vector2> Points { get; set; }
public float[] Sizes { get; set; }
float static_CalcSize_Mid;
/// <summary>
/// 计算画笔大小
/// </summary>
float static_CalcSize_PenSize;
public void CalcSize()
{
if (Points.Count < )
return;
Sizes = new float[Points.Count];
for (i = ; i <= Points.Count - ; i++) {
static_CalcSize_Mid = Convert.ToSingle(Math.Abs(i - Points.Count / ));
static_CalcSize_PenSize = - static_CalcSize_Mid / Points.Count * ;
Sizes[i] = static_CalcSize_PenSize;
}
}
}

C#-PointSequence

视频

附录

  GitHub:EDGameEngine.AutoDraw

  早期博客:程序如何实现自动绘图

  早期博客:更优秀的自动绘图程序

  创意分享:儿童涂鸦遇上程序绘图

自动绘图AI:程序如何画出动漫美少女的更多相关文章

  1. 人才需求之Java程序员与AI程序员

    据100offer报告显示:2018年Java人才市场「高开低走」的动荡局势.整体求职难度变大,且全年波动更剧烈,淡旺季区别明显.企业发出的Java面邀总数几个季度连续下跌,Q4 甚至比去年同期下降了 ...

  2. MFC文档(SDI)应用:画图程序(画圆、画线、鼠标事件)

    要求 1. 在客户区输出一条顺时针45度的直线.一个正方形.一个大圆: 2. 在客户区输出一个图标: 3. 当按下鼠标左键时,将以鼠标坐标为圆心画直径为20个单位的小圆. 首先设置两个变量,用来保存颜 ...

  3. 师傅领进门之6步教你跑通一个AI程序!

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云计算基础发表于云+社区专栏 源码下载地址请点击原文查看. 初学机器学习,写篇文章mark一下,希望能为将入坑者解点惑.本文介绍一些机 ...

  4. android程序启动画面之Splash总结[转]

    方法一: 很多应用都会有一个启动界面.欢迎画面慢慢隐现,然后慢慢消隐.实现这种效果的方法有两种(暂时只发现两种)1.使用两个Activity,程序启动时候load第一张Activity,然后由tick ...

  5. C++ 之 简单的五子棋AI程序

    本人是大一新生,寒假无聊,抱着试试看的心态(没有想到可以完成),写了C++的简单五子棋程序,开心.     下面是效果图:     一.首先讲讲大致思路.            五子棋实现的基础:  ...

  6. 小程序-生成一个小程序码画在canvas画布上生成一张图片分享出去

    这个需求我遇到过2次.一次是在识别二维码后跳转到其它页面,另一次是识别二维码后进入到生成小程序码的当前页面. 我有一个梦想,就是成为一名黑客!!!!!! 小程序中js wx.request({     ...

  7. 用python程序来画花

    from turtle import * import time setup(600,800,0,0) speed(0) penup() seth(90) fd(340) seth(0) pendow ...

  8. 微信小程序css画三角形内有文字

    <view class="productStatus"> <span> <em>已上架</em> </span> < ...

  9. unity美少女动作RPG游戏源码Action-RPG Starter Kit v5.0a

    功能完整的ARPG游戏模板 Core Features!! - Combat System - Skill Tree - Enemy AI - Save-Load Game - Shop System ...

随机推荐

  1. Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...

  2. Python的单元测试(二)

    title: Python的单元测试(二) date: 2015-03-04 19:08:20 categories: Python tags: [Python,单元测试] --- 在Python的单 ...

  3. Unity 序列化 总结

    查找了 Script Serialization http://docs.unity3d.com/Manual/script-Serialization.html 自定义序列化及例子: http:// ...

  4. CSS 特殊属性介绍之 pointer-events

    首先看一下 MDN 上关于 pointer-events 的介绍: CSS属性 pointer-events 允许作者控制特定的图形元素在何时成为鼠标事件的 target.当未指定该属性时,SVG 内 ...

  5. [C#] 了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数

    了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数 目录 简介 特点 方法的参数 方法的返回值 与批处理交互的一个示例 简介 我们知道,新建一个控制台应用程序的时候,IDE 会同时创建 ...

  6. 一行代码实现java list去重

    1.不带类型写法: 1 List listWithoutDup = new ArrayList(new HashSet(listWithDup)); 2.带类型写法(以String类型为例):1)Ja ...

  7. 【读书】PHP程序员要读的书目(不断完善中)

    本文地址 分享提纲: 1. PHP 2. Linux 3. Apache/Nginx 4. Mysql 5.设计模式/架构 6. 缓存并发 7. 其他语言 8. 代码基础 9. 大前端 10. 管理生 ...

  8. css样式之background详解

    background用法详解: 1.background-color 属性设置元素的背景颜色 可能的值 color_name            规定颜色值为颜色名称的背景颜色(比如 red) he ...

  9. WINDOWS系统下MYSQL安装过程中的注意事项

    1.首先MySQL的安装方式有两种:一种是MSI安装方式,很简单就像安装Windows软件一样.另外一种就是ZIP安装方式.这种相对而言比较麻烦.新手推荐MSI安装方式. 安装方式有以下两种: MSI ...

  10. linux中kvm的安装及快照管理

    一.kvm的安装及状态查看 1.安装软件 yum -y install kvm virt-manager libvirt2.启动libvirtd 报错,升级device-mapper-libs yum ...