C語(yǔ)言實(shí)現(xiàn)繪制繞線畫(huà)的示例代碼
繞線畫(huà)簡(jiǎn)介
簡(jiǎn)單點(diǎn)來(lái)說(shuō),就是在木板上釘一圈釘子,通過(guò)繞線進(jìn)行構(gòu)圖,最終呈現(xiàn)出一幅圖像。
算法簡(jiǎn)介
可以總結(jié)概括一下,
首先需要有一張圖,可以是彩色的,但是必須顏色比較分明。
對(duì)圖像進(jìn)行灰度處理。
隨機(jī)生成 n 組數(shù),就是每?jī)蓚€(gè)釘子的組合。
計(jì)算 n 組數(shù)據(jù)連線所過(guò)圖像像素的平均數(shù),求出最小的一組。
連接該組釘子,并對(duì)這條線經(jīng)過(guò)的像素值分別加 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è)置每次隨機(jī)生成連接的數(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; // 起點(diǎn) int EndP; // 終點(diǎn) }; PointS points[PointNum]; LineS lines[RandNum]; // 為了判斷兩點(diǎn)是否連線定義的一維數(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]; } // 儲(chǔ)存已經(jīng)繪制過(guò)的線 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ī)生成的進(jìn)行初始化 void Line2False() { for (int i = 0; i < (1 + PointNum) * PointNum / 2; i++) { Line_Rand[i] = false; } } // 判斷這兩個(gè)點(diǎn)是否連線 bool IsLinked(int p1, int p2) { if (p1 >= p2) return LineXY[(1 + p1) * p1 / 2 + p2]; else return LineXY[(1 + p2) * p2 / 2 + p1]; } // 儲(chǔ)存已經(jīng)繪制過(guò)的線 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(); // 初始化點(diǎn) void ColorToGray(IMAGE *pimg); // 彩色圖像轉(zhuǎn)換為灰度圖像 void Random(); // 產(chǎn)生隨機(jī)數(shù) LineS Getline(IMAGE *pimg); // 獲取照片顏色 void ToColor(IMAGE *oriPic, IMAGE *linePic); // 給繞線圖賦予顏色 int main() { initgraph(SIZE, SIZE); setbkcolor(WHITE); cleardevice(); IMAGE imgpolt; // 加載原圖 IMAGE oriPic; // 儲(chǔ)存原圖 IMAGE linePic; // 線圖 loadimage(&imgpolt, _T("TG.jpeg"), SIZE, SIZE); // 加載原圖 oriPic = imgpolt; // 原圖 ColorToGray(&imgpolt); // 將圖片轉(zhuǎn)換為灰度 InitPoints(); // 初始化點(diǎn) srand((unsigned)time(NULL)); // 生成隨機(jī)種子 for (int i = 0; i < LineNum; i++) { Random(); // 隨機(jī)生成 80 LineS myline = Getline(&imgpolt); // 計(jì)算 80 組點(diǎn)中平均值最小的 Link(myline.StarP, myline.EndP); // 記錄繪制過(guò)的線防止重復(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; } // 初始化點(diǎn)(想創(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); } } // 隨機(jī)生成線 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); // 記錄隨機(jī)生成的線 } 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語(yǔ)言實(shí)現(xiàn)繪制繞線畫(huà)的示例代碼的文章就介紹到這了,更多相關(guān)C語(yǔ)言繞線畫(huà)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++使用new和delete進(jìn)行動(dòng)態(tài)內(nèi)存分配與數(shù)組封裝
這篇文章主要介紹了C++使用new和delete進(jìn)行動(dòng)態(tài)內(nèi)存分配與數(shù)組封裝,運(yùn)行期間才能確定所需內(nèi)存大小,此時(shí)應(yīng)該使用new申請(qǐng)內(nèi)存,下面我們就進(jìn)入文章學(xué)習(xí)具體的操作方法,需要的小伙伴可以參考一下2022-03-03c++實(shí)現(xiàn)通用參數(shù)解析類示例
使用命令行執(zhí)行程序的時(shí)候在程序后可跟多個(gè)參數(shù)列表,而main函數(shù)的argc和argv分別存儲(chǔ)了相關(guān)的參數(shù)個(gè)數(shù)和參數(shù)內(nèi)容,而循環(huán)輸入相關(guān)的時(shí)候就需要用戶自己來(lái)解析相關(guān)參數(shù)。以下代碼用c++的方式實(shí)現(xiàn)了相關(guān)解析的封裝,使用起來(lái)非常方便2014-03-03C語(yǔ)言函數(shù)調(diào)用約定和返回值詳情
這篇文章主要介紹了C語(yǔ)言函數(shù)調(diào)用約定和返回值詳情,函數(shù)調(diào)用約定不同,會(huì)影響函數(shù)生成的符號(hào)名,函數(shù)入?yún)㈨樞颍螀?nèi)存的清理者,更多相關(guān)需要的小伙伴可以參考下文詳情介紹2022-07-07C語(yǔ)言fgetc和fputc函數(shù)用法詳解(以字符形式讀寫(xiě)文件)
這篇文章主要介紹了C語(yǔ)言fgetc和fputc函數(shù)用法詳解(以字符形式讀寫(xiě)文件),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之vector底層實(shí)現(xiàn)機(jī)制解析
向量(Vector)是一個(gè)封裝了動(dòng)態(tài)大小數(shù)組的順序容器(Sequence?Container)。跟任意其它類型容器一樣,它能夠存放各種類型的對(duì)象??梢院?jiǎn)單的認(rèn)為,向量是一個(gè)能夠存放任意類型的動(dòng)態(tài)數(shù)組2021-11-11C++利用兩個(gè)棧實(shí)現(xiàn)隊(duì)列的方法
這篇文章主要給大家介紹了關(guān)于C++利用兩個(gè)棧實(shí)現(xiàn)隊(duì)列的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05詳解如何利用C++實(shí)現(xiàn)Mystring類
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)MyString的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08C語(yǔ)言深入探究水仙花數(shù)與變種水仙花數(shù)代碼
求水仙花數(shù)和變種水仙花數(shù)是非常適合初學(xué)者學(xué)習(xí)的代碼,其中包含的循環(huán)和邏輯方式等知識(shí)點(diǎn)。這既能起到對(duì)以往知識(shí)的復(fù)習(xí),也可以學(xué)習(xí)到一種不同的邏輯思考方式2022-05-05C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)順序表中的增刪改(頭插頭刪)教程示例詳解
這篇文章主要為大家介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)順序表中增刪改關(guān)于頭插頭刪的教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02