说明
本文发布较早,了解最新动态,请查看 GitHub 项目。(2022 年 6 月 注)
准备
全新的图形引擎与 AI 算法,高效流畅地绘出任何一副美丽的图像。
IDE:VisualStudio
Language:VB.NET / C#
Graphics:AutoPaint.NET
第一节 背景
背景是图画里衬托主体事物的景象。
![](https://images2015.cnblogs.com/blog/726127/201609/726127-20160926235959516-1313971549.png)
图1-1 先画个蓝蓝的天空
蓝天、白云和大地,程序最擅长这种色调单一的涂抹了。
第二节 轮廓
轮廓是物体的外周或图形的外框。
![](https://images2015.cnblogs.com/blog/726127/201609/726127-20160927000054110-1520272797.png)
图2-2 勾勒人物和衣饰轮廓
现在 AI 要控制笔触大小和颜色,让图像的主体显现出来。
第三节 光影
光影是物体在光的照射下呈现出明与暗的关系。
![](https://images2015.cnblogs.com/blog/726127/201609/726127-20160927000120797-541557585.png)
图3-1 光影提升画面质感
AI 可不懂什么是光影,在上一步的基础上优化细节即可。
第四节 润色
润色是增加物体本身及其周围的色彩。
![](https://images2015.cnblogs.com/blog/726127/201609/726127-20160927000201594-911557542.png)
图4-1 画面润色
这是关键一步,AI需要将丢失的颜色细节补缺回来。
第五节 成型
大功告成!前面所有的步骤都是为这一步铺垫。
![](https://images2015.cnblogs.com/blog/726127/201609/726127-20160927000232391-1911750027.png)
图5-1 人物已经栩栩如生啦
事实上 AI 只进行这一步也可以画出完整的图像,但没有过渡会显得生硬。
第六节 算法
算法思路很简单,计算画笔轨迹后一遍遍重绘,感觉上是人类画手的效果。
不再是二值化
因为现在要绘制全彩图像,将图像划分为只有黑和白的效果已经没有什么意义,二值化不再适用
适用的方法是将 RGB 颜色空间划分为若干个颜色子空间,然后逐个处理一幅图像中属于某个子空间的区域
自动循迹
循迹算法没有大的变动,仍是早前博客里贴出的代码
彩色图像线条较短,可以不再计算点周围的权值用来中断轨迹
重绘
程序先选择笔触较大、颜色淡的画笔绘制一遍,然后在这基础上逐步减小笔触并加深色彩
直接按照标准笔触可以一遍成型,但会显得突兀和生硬,毕竟这个AI不是真的在思考如何画一幅图像
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Imports System.Numerics
'''
''' 表示自动循迹并生成绘制序列的AI
'''
Public Class SequenceAI
'''
''' 线条序列List
'''
'''
Public Property Sequences As List(Of PointSequence)
'''
''' 扫描方式
'''
Public Property ScanMode As ScanMode = ScanMode.Rect
Dim xArray() As Integer = {-1, 0, 1, 1, 1, 0, -1, -1}
Dim yArray() As Integer = {-1, -1, -1, 0, 1, 1, 1, 0}
Dim NewStart As Boolean
'''
''' 创建并初始化一个可自动生成绘制序列AI的实例
'''
Public Sub New(BolArr(,) As Integer)
Sequences = New List(Of PointSequence)
CalculateSequence(BolArr)
For Each SubItem In Sequences
SubItem.CalcSize()
Next
End Sub
'''
''' 新增一个序列
'''
Private Sub CreateNewSequence()
Sequences.Add(New PointSequence)
End Sub
'''
''' 在序列List末尾项新增一个点
'''
Private Sub AddPoint(point As Vector2)
Sequences.Last.Points.Add(point)
End Sub
'''
''' 计算序列
'''
Private Sub CalculateSequence(BolArr(,) As Integer)
If ScanMode = ScanMode.Rect Then
ScanRect(BolArr)
Else
ScanCircle(BolArr)
End If
End Sub
'''
''' 圆形扫描
'''
'''
Private Sub ScanCircle(BolArr(,) As Integer)
Dim xCount As Integer = BolArr.GetUpperBound(0)
Dim yCount As Integer = BolArr.GetUpperBound(1)
Dim CP As New Point(xCount / 2, yCount / 2)
Dim R As Integer = 0
For R = 0 To If(xCount > yCount, xCount, yCount)
For Theat = 0 To Math.PI * 2 Step 1 / 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 > 0 And dy > 0 And dx < xCount And dy < yCount) Then Continue For
If BolArr(dx, dy) = 1 Then
BolArr(dx, dy) = 0
Me.CreateNewSequence()
Me.AddPoint(New Vector2(dx, dy))
CheckMove(BolArr, dx, dy, 0)
NewStart = True
End If
Next
Next
End Sub
'''
''' 矩形扫描
'''
'''
Private Sub ScanRect(BolArr(,) As Integer)
Dim xCount As Integer = BolArr.GetUpperBound(0)
Dim yCount As Integer = BolArr.GetUpperBound(1)
For i = 0 To xCount - 1
For j = 0 To yCount - 1
Dim dx As Integer = i
Dim dy As Integer = j
If Not (dx > 0 And dy > 0 And dx < xCount And dy < yCount) Then Continue For
If BolArr(dx, dy) = 1 Then
BolArr(dx, dy) = 0
Me.CreateNewSequence()
Me.AddPoint(New Vector2(dx, dy))
CheckMove(BolArr, dx, dy, 0)
NewStart = True
End If
Next
Next
End Sub
'''
''' 递归循迹算法
'''
Private Sub CheckMove(ByRef bolArr(,) As Integer, ByVal x As Integer, ByVal y As Integer, ByVal StepNum As Integer)
If StepNum > 1000 Then Return
Dim xBound As Integer = bolArr.GetUpperBound(0)
Dim yBound As Integer = bolArr.GetUpperBound(1)
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 = 0 To 7
dx = x + xArray(i)
dy = y + yArray(i)
If Not (dx > 0 And dy > 0 And dx < xBound And dy < yBound) Then
Return
ElseIf bolArr(dx, dy) = 1 Then
bolArr(dx, dy) = 0
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 + 1)
NewStart = True
End If
Next
End Sub
'''
''' 返回点权值
'''
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(0)
Dim yBound As Integer = BolArr.GetUpperBound(1)
For i = 0 To 7
dx = x + xArray(i)
dy = y + yArray(i)
If dx > 0 And dy > 0 And dx < xBound And dy < yBound Then
If BolArr(dx, dy) = 1 Then
ResultValue += 1
End If
End If
Next
Return ResultValue
End Function
End Class
'''
''' 线条扫描方式
'''
Public Enum ScanMode
'''
''' 矩形扫描
'''
Rect
'''
''' 圆形扫描
'''
Circle
End Enum
VB.NET-SequenceAI
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Imports System.Numerics
'''
''' 表示由一系列点向量组成的线条
'''
Public Class PointSequence
Public Property Points As New List(Of Vector2)
Public Property Sizes As Single()
'''
''' 计算画笔大小
'''
Public Sub CalcSize()
If Points.Count < 1 Then Exit Sub
Static Mid, PenSize As Single
ReDim Sizes(Points.Count - 1)
For i = 0 To Points.Count - 1
Mid = CSng(Math.Abs(i - Points.Count / 2))
PenSize = 1 - Mid / Points.Count * 2
Sizes(i) = PenSize
Next
End Sub
End Class
VB.NET-PointSequence
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Numerics;
///
/// 表示自动循迹并生成绘制序列的AI
///
public class SequenceAI
{
///
/// 线条序列List
///
///
public List Sequences { get; set; }
///
/// 扫描方式
///
public ScanMode ScanMode { get; set; }
int[] xArray = {
-1,
0,
1,
1,
1,
0,
-1,
-1
};
int[] yArray = {
-1,
-1,
-1,
0,
1,
1,
1,
0
};
bool NewStart;
///
/// 创建并初始化一个可自动生成绘制序列AI的实例
///
public SequenceAI(int[,] BolArr)
{
Sequences = new List();
CalculateSequence(BolArr);
foreach (object SubItem_loopVariable in Sequences) {
SubItem = SubItem_loopVariable;
SubItem.CalcSize();
}
}
///
/// 新增一个序列
///
private void CreateNewSequence()
{
Sequences.Add(new PointSequence());
}
///
/// 在序列List末尾项新增一个点
///
private void AddPoint(Vector2 point)
{
Sequences.Last.Points.Add(point);
}
///
/// 计算序列
///
private void CalculateSequence(int[,] BolArr)
{
if (ScanMode == ScanMode.Rect) {
ScanRect(BolArr);
} else {
ScanCircle(BolArr);
}
}
///
/// 圆形扫描
///
///
private void ScanCircle(int[,] BolArr)
{
int xCount = BolArr.GetUpperBound(0);
int yCount = BolArr.GetUpperBound(1);
Point CP = new Point(xCount / 2, yCount / 2);
int R = 0;
for (R = 0; R yCount ? xCount : yCount; R++) {
for (Theat = 0; Theat 0 & dy > 0 & dx < xCount & dy 2 AndAlso AroundValue < 8 Then
//Return
//End If
for (i = 0; i 0 & dy > 0 & dx < xBound & dy 0 & dx < xBound & dy |