说明

  本文发布较早,查看最新动态,请关注 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. C# 破解 Reflector8.5

    一.分析 破解.net .dll,可以使用reflector,但官方提供的reflector是需要购买的,因此,破解reflector势在必行. 二.破解Reflector具体步骤 下面为详细的破解步 ...

  2. Android Studio开发RecyclerView遇到的各种问题以及解决(一)

    以前一直在用ListView,,,最近才看RecyclerView发现好强大.RecyclerView前提是Android版本在5.0以上,本人以前用的是eclipse只支持到4.4.索性就安装一个A ...

  3. Atitit.cto 与技术总监的区别

    Atitit.cto 与技术总监的区别 1. 核心区别1 2. Cto主要职责1 3. 如何提升到cto1 4. CTO五种基本的必备素质:2 5. 2 1. 核心区别 技术总监(Chief Tech ...

  4. 浅谈单片机中C语言与汇编语言的转换

    做了一单片机设计,要用C语言与汇编语言同时实现,现将这次设计的感受和收获,还有遇到的问题写下,欢迎感兴趣的朋友交流想法,提出建议. 单片机设计:基于51单片机的99码表设计 软件环境:Proteus8 ...

  5. 【微信SEO】公众号也能做排名?

    [写于2016年8月] 最近,微信团队发出一则公告,开放公众号运营者一年内更改公众号名一次,这对不少名字起的奇葩名字(包括dkplus)的公众号来说是一件好事. 为什么说是好事呢?公众号名字直接关联到 ...

  6. [LeetCode] Longest Substring with At Least K Repeating Characters 至少有K个重复字符的最长子字符串

    Find the length of the longest substring T of a given string (consists of lowercase letters only) su ...

  7. ubuntu15 coreclr

    看了很多文章心里痒痒,也下载个ubuntu想发布个asp.net5试试,自然是下载的最新版本15.结果涉及dnu restore,dnx...什么的都没反应,切换为mono就正常,奇怪了,按说core ...

  8. Dubbo学习小记

    前言 周一入职的新公司,到了公司第一件事自然是要熟悉新公司使用的各种技术,搭建本地的环境. 熟悉新公司技术的过程中,首先就是Maven,这个前面已经写过文章了,然后就是Dubbo----公司的服务都是 ...

  9. 《图解TCP/IP》读书笔记

    一.国际惯例:书托 这是一本图文并茂的网络管理技术书籍,旨在让广大读者理解TCP/IP的基本知识.掌握TCP/IP的基本技能. 书中讲解了网络基础知识.TCP/IP基础知识.数据链路.IP协议.IP协 ...

  10. 【完全开源】知乎日报UWP版:项目结构说明、关键源代码解释

    目录 说明 项目结构 关键代码 演示视频 说明 上一篇博客将源码放出来了,但是并没有做过多的介绍,所以如果自己硬看可能需要花费很长的时间,尤其这些代码并不是自己写的.项目不算复杂但是也不算简单,这篇文 ...