C語言實現(xiàn)繪制繞線畫的示例代碼
繞線畫簡介
簡單點來說,就是在木板上釘一圈釘子,通過繞線進(jìn)行構(gòu)圖,最終呈現(xiàn)出一幅圖像。
算法簡介
可以總結(jié)概括一下,
首先需要有一張圖,可以是彩色的,但是必須顏色比較分明。
對圖像進(jìn)行灰度處理。
隨機生成 n 組數(shù),就是每兩個釘子的組合。
計算 n 組數(shù)據(jù)連線所過圖像像素的平均數(shù),求出最小的一組。
連接該組釘子,并對這條線經(jīng)過的像素值分別加 m。
重復(fù)前面步驟 3 到步驟 5 直到繪制 z 條線結(jié)束循環(huán)。
示例


#include<graphics.h>
#include<math.h>
#include<conio.h>
#include<time.h>
// 以下數(shù)據(jù)可以自己調(diào)節(jié)
#define PointNum 288 // 圓圈分的數(shù)量(一圈釘子的數(shù)量)
#define LineNum 3000 // 循環(huán)繪制線的數(shù)量
#define RandNum 120 // 設(shè)置每次隨機生成連接的數(shù)量
#define AddColor 52 // 增加的值 0 到 255 值越小越線越集中,越大越分散
#define SIZE 800 // 圖像大小
// 以下參數(shù)不用調(diào)節(jié)
#define PI acos(-1.0) // 圓周率
#define R (SIZE / 2 - 10) // 半徑
struct PointS
{
int p;
int x;
int y;
};
struct LineS
{
int StarP; // 起點
int EndP; // 終點
};
PointS points[PointNum];
LineS lines[RandNum];
// 為了判斷兩點是否連線定義的一維數(shù)組
bool LineXY[(1 + PointNum) * PointNum / 2] = { false };
bool Line_Rand[(1 + PointNum) * PointNum / 2] = { false };
// 兩條線是否連接
bool IsLineKed_Rand(int p1, int p2)
{
if (p1 >= p2)
return Line_Rand[(1 + p1) * p1 / 2 + p2];
else
return Line_Rand[(1 + p2) * p2 / 2 + p1];
}
// 儲存已經(jīng)繪制過的線
void Link_Rand(int p1, int p2)
{
if (p1 >= p2)
Line_Rand[(1 + p1) * p1 / 2 + p2] = true;
else
Line_Rand[(1 + p2) * p2 / 2 + p1] = true;
}
// 將隨機生成的進(jìn)行初始化
void Line2False()
{
for (int i = 0; i < (1 + PointNum) * PointNum / 2; i++)
{
Line_Rand[i] = false;
}
}
// 判斷這兩個點是否連線
bool IsLinked(int p1, int p2)
{
if (p1 >= p2)
return LineXY[(1 + p1) * p1 / 2 + p2];
else
return LineXY[(1 + p2) * p2 / 2 + p1];
}
// 儲存已經(jīng)繪制過的線
void Link(int p1, int p2)
{
if (p1 >= p2)
LineXY[(1 + p1) * p1 / 2 + p2] = true;
else
LineXY[(1 + p2) * p2 / 2 + p1] = true;
}
int Round(float x); // 取整
void InitPoints(); // 初始化點
void ColorToGray(IMAGE *pimg); // 彩色圖像轉(zhuǎn)換為灰度圖像
void Random(); // 產(chǎn)生隨機數(shù)
LineS Getline(IMAGE *pimg); // 獲取照片顏色
void ToColor(IMAGE *oriPic, IMAGE *linePic); // 給繞線圖賦予顏色
int main()
{
initgraph(SIZE, SIZE);
setbkcolor(WHITE);
cleardevice();
IMAGE imgpolt; // 加載原圖
IMAGE oriPic; // 儲存原圖
IMAGE linePic; // 線圖
loadimage(&imgpolt, _T("TG.jpeg"), SIZE, SIZE); // 加載原圖
oriPic = imgpolt; // 原圖
ColorToGray(&imgpolt); // 將圖片轉(zhuǎn)換為灰度
InitPoints(); // 初始化點
srand((unsigned)time(NULL)); // 生成隨機種子
for (int i = 0; i < LineNum; i++)
{
Random(); // 隨機生成 80
LineS myline = Getline(&imgpolt); // 計算 80 組點中平均值最小的
Link(myline.StarP, myline.EndP); // 記錄繪制過的線防止重復(fù)繪制
line(points[myline.StarP].x, points[myline.StarP].y, points[myline.EndP].x, points[myline.EndP].y);
}
_getch();
// /*該部分是給線條加上顏色*/
// saveimage(_T("test.png")); // 保存一下繞線圖
// loadimage(&linePic, _T("test.png"), SIZE, SIZE); // 重新加載繞線圖
// ToColor(&oriPic, &linePic); // 用原圖將繞線圖的顏色替換
// putimage(0, 0, &oriPic);
// _getch();
return 0;
}
// 初始化點(想創(chuàng)新可以生成橢圓的位置坐標(biāo))
void InitPoints()
{
for (int i = 0; i < PointNum; i++)
{
double a = i * PI * 2 / PointNum;
points[i].p = i;
points[i].x = int(SIZE / 2.0 + R * cos(a));
points[i].y = int(SIZE / 2.0 - R * sin(a));
setlinecolor(BLACK);
circle(points[i].x, points[i].y, 3);
}
}
// 彩色圖像轉(zhuǎn)換為灰度圖像
void ColorToGray(IMAGE *pimg)
{
DWORD *p = GetImageBuffer(pimg); // 獲取顯示緩沖區(qū)指針
COLORREF c;
for (int i = pimg->getwidth() * pimg->getheight() - 1; i >= 0; i--)
{
c = BGR(p[i]);
c = (GetRValue(c) * 299 + GetGValue(c) * 587 + GetBValue(c) * 114 + 500) / 1000;
p[i] = RGB(c, c, c);
}
}
// 隨機生成線
void Random()
{
for (int i = 0; i < RandNum; i++)
{
int starP;
int endP;
while (true)
{
starP = rand() % PointNum;
endP = rand() % PointNum;
if (IsLinked(starP, endP) == false
&& IsLineKed_Rand(starP, endP) == false)
{
break;
}
}
lines[i].StarP = starP;
lines[i].EndP = endP;
Link_Rand(starP, endP); // 記錄隨機生成的線
}
Line2False(); // 初始化線值
}
// 四舍五入
int Round(float x)
{
return (int)(x < 0 ? x - 0.5 : x + 0.5);
}
// 獲取顏色最深的那一條線
LineS Getline(IMAGE *pimg)
{
LineS mylines;
mylines.StarP = 0;
mylines.EndP = 0;
DWORD* p_data = GetImageBuffer(pimg);
int width = pimg->getwidth();
double MaxNum = 255;
int MYsteps;
float X, Y;
float CX, CY;
for (int i = 0; i<RandNum; i++)
{
int SUMDN = 0;
int x1 = points[lines[i].StarP].x;
int y1 = points[lines[i].StarP].y;
int x2 = points[lines[i].EndP].x;
int y2 = points[lines[i].EndP].y;
int steps = abs(x2 - x1) > abs(y2 - y1) ? abs(x2 - x1) : abs(y2 - y1);
float x = (float)x1;
float y = (float)y1;
float cx = (float)(x2 - x1) / steps;
float cy = (float)(y2 - y1) / steps;
for (int j = 0; j < steps; j++)
{
int XIA = width * Round(y) + Round(x);
SUMDN += GetRValue(p_data[XIA]);
x += cx;
y += cy;
}
double Aver = SUMDN / (steps * 1.0);
if (Aver < MaxNum)
{
MaxNum = Aver;
mylines = lines[i];
MYsteps = steps;
X = (float)x1;
Y = (float)y1;
CX = cx;
CY = cy;
}
}
if (MaxNum == 255)
{
return mylines;
}
for (int j = 0; j < MYsteps; j++)
{
int XIA = width* Round(Y) + Round(X);
int c = GetRValue(p_data[XIA]) + AddColor > 255 ? 255 : GetRValue(p_data[XIA]) + AddColor;
p_data[XIA] = RGB(c, c, c);
X += CX;
Y += CY;
}
return mylines;
}
// 給線圖上色
void ToColor(IMAGE *oriPic, IMAGE *linePic)
{
DWORD* ori_data = GetImageBuffer(oriPic);
DWORD* line_data = GetImageBuffer(linePic);
for (int i = oriPic->getwidth() * oriPic->getheight() - 1; i >= 0; i--)
{
int oriR = GetRValue(ori_data[i]);
int oriG = GetGValue(ori_data[i]);
int oriB = GetBValue(ori_data[i]);
int lineR = GetRValue(line_data[i]);
int lineG = GetGValue(line_data[i]);
int lineB = GetBValue(line_data[i]);
int newPicR = (int)(255 - (255 - lineR)*(255 - oriR) / 255.0);
int newPicG = (int)(255 - (255 - lineG)*(255 - oriG) / 255.0);
int newPicB = (int)(255 - (255 - lineB)*(255 - oriB) / 255.0);
ori_data[i] = RGB(newPicR, newPicG, newPicB);
}
}到此這篇關(guān)于C語言實現(xiàn)繪制繞線畫的示例代碼的文章就介紹到這了,更多相關(guān)C語言繞線畫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++使用new和delete進(jìn)行動態(tài)內(nèi)存分配與數(shù)組封裝
這篇文章主要介紹了C++使用new和delete進(jìn)行動態(tài)內(nèi)存分配與數(shù)組封裝,運行期間才能確定所需內(nèi)存大小,此時應(yīng)該使用new申請內(nèi)存,下面我們就進(jìn)入文章學(xué)習(xí)具體的操作方法,需要的小伙伴可以參考一下2022-03-03
C語言fgetc和fputc函數(shù)用法詳解(以字符形式讀寫文件)
這篇文章主要介紹了C語言fgetc和fputc函數(shù)用法詳解(以字符形式讀寫文件),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
C語言數(shù)據(jù)結(jié)構(gòu)之vector底層實現(xiàn)機制解析
向量(Vector)是一個封裝了動態(tài)大小數(shù)組的順序容器(Sequence?Container)。跟任意其它類型容器一樣,它能夠存放各種類型的對象。可以簡單的認(rèn)為,向量是一個能夠存放任意類型的動態(tài)數(shù)組2021-11-11
C語言深入探究水仙花數(shù)與變種水仙花數(shù)代碼
求水仙花數(shù)和變種水仙花數(shù)是非常適合初學(xué)者學(xué)習(xí)的代碼,其中包含的循環(huán)和邏輯方式等知識點。這既能起到對以往知識的復(fù)習(xí),也可以學(xué)習(xí)到一種不同的邏輯思考方式2022-05-05
C語言數(shù)據(jù)結(jié)構(gòu)順序表中的增刪改(頭插頭刪)教程示例詳解
這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)順序表中增刪改關(guān)于頭插頭刪的教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02

