C语言使用LKH算法创新一种图像处理

教育   2024-07-09 23:57   宁夏  

前面文章中使用LKH算法解决TSP问题我已详细说明,我又重新想了一种使用LKH算法的创新,就是能否使用这个算法来实现一个图像处理,将照片生成点图,然后使用LKH算法进行连接,真正实现一笔画。

理论是成立的,接下来就是实践,我的思路如下:首先我们需要一张图片,然后将这个图片进行灰度处理,定义一个结构体来存储生成的点,在照片中生成随机点,如果点的位置的灰度值大于某一个值,就记录到结构体中,为了避免重复点生成,我们还需要再做一个判断,判断这个点的位置和已生成点的位置是否很近,如果距离很近,则重新生成。这里可以借鉴我前面发表的锤画代码,当生成的点足够密集,我们需要将这些点使用LKH算法连接即可。

为了能够很好地展示这个算法的效果这里使用罗中立的油画父亲。

生成点图如下

点图存在的问题就是生成的密集地方点重合了或者离得太近,这里进行代码的重新调整。

通过控制生成点与点之间的距离,可以看到生成的效果明显有所改善。

接下来就可以使用生成的点连接起来,看看效果。

然后生成了8000多点开始解算最短路径!

漫长的等待中。。。

经过漫长的等待,总算是出来结果了。

结果虽然很抽象,但是可以说是智慧的结晶。

///////////////////////////////////////////////////// 程序名称:LKH算法生成的图像处理// 编译环境:Mictosoft Visual Studio 2022, EasyX_20200315(beta)// 作  者:luoyh <2864292458@qq.com>(V:louyh1207)// 公 众 号:C语言研究// 最后修改:2024-7-9//#define  _CRT_SECURE_NO_WARNINGS#include <graphics.h>  // 引入EasyX图形库  #include <conio.h>  #include <stdlib.h>  #include <time.h>  #include <math.h>#include <stdio.h>  #include <string.h>  #include <Windows.h>
#define CLOSE_THRESHOLD 6.0 // 定义“很近”的阈值为10个单位
typedef struct{ int x; int y;} Point;
// 计算两点之间的距离 double distance(Point p1, Point p2){ return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));}
// 检查新点是否与已有点距离很近 int isClose(Point newPoint, Point* points, int numPoints){ for (int i = 0; i < numPoints; i++) { if (distance(newPoint, points[i]) < CLOSE_THRESHOLD) { return 1; // 表示很近 } } return 0; // 表示不近 }// 彩色图像转换为灰度图像void ColorToGray(IMAGE* pimg){ DWORD* p = GetImageBuffer(pimg); // 获取显示缓冲区指针 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); // 由于是灰度值,无需再执行 BGR 转换 }}int H;int W;void generateRandomCoordinate(int* x, int* y){ *x = rand() % W; *y = rand() % H;}int GetColorNumber(int x, int y, IMAGE* pimg){ DWORD* p = GetImageBuffer(pimg); // 获取显示缓冲区指针 COLORREF c; c = BGR(p[y * W + x]); int X = GetRValue(c); return X;}
int RGBNUMS[256];bool isover[256];void GetRGBNUMS(){ int MaxNum = 15000; RGBNUMS[0] = 0; for (int i = 0; i < 256; i++) { RGBNUMS[i] = RGBNUMS[i - 1] + (MaxNum - RGBNUMS[i - 1]) / 11000; }}bool is_over(){ for (int i = 1; i < 256; i++) { if (isover[i] == false) { return false; } } return true;}
int* tour = NULL;
void WriteBat(){ const char* batContent = "@echo off\r\npath=%path%;\r\nLKH-3.exe Par.par\r\n"; const char* fileName = "LKH.bat"; FILE* file = fopen(fileName, "w"); if (file == NULL) { perror("无法打开文件"); return; } fprintf(file, "%s", batContent); fclose(file);}
void WritePar(){ const char* parContent = "PROBLEM_FILE = Tsp.tsp\r\n" "MOVE_TYPE = 5\r\n" "PATCHING_C = 3\r\n" "PATCHING_A = 2\r\n" "RUNS = 1\r\n" "TOUR_FILE = ./output.txt\r\n"; const char* fileName = "Par.par"; FILE* file = fopen(fileName, "w"); if (file == NULL) { perror("Failed to open file"); return; } fprintf(file, "%s", parContent); fclose(file);}
void WriteTsp(Point* Photo_list, int Photo_list_size){ int POINTS = Photo_list_size;
char tspContent[409600]; // 假设内容不会超过4096个字符 char buffer[256]; strcpy(tspContent, "NAME : TSPDATA\r\n"); strcat(tspContent, "COMMENT : drilling problem (Ludwig)\r\n"); strcat(tspContent, "TYPE : TSP\r\n"); sprintf(buffer, "DIMENSION : %d\r\n", POINTS); strcat(tspContent, buffer); strcat(tspContent, "EDGE_WEIGHT_TYPE : EUC_2D\r\n"); strcat(tspContent, "NODE_COORD_SECTION\r\n");
for (int i = 0; i < Photo_list_size; i++) { sprintf(buffer, "%d %d %d\r\n", i + 1, Photo_list[i].x, Photo_list[i].y); strcat(tspContent, buffer); } strcat(tspContent, "EOF\r\n"); const char* fileName = "Tsp.tsp"; FILE* file = fopen(fileName, "w"); if (file == NULL) { perror("Failed to open file"); return; } fprintf(file, "%s", tspContent); fclose(file);}
void StartProcess(const char* filePath){ STARTUPINFOA si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; // 不显示窗口 ZeroMemory(&pi, sizeof(pi));
// 创建进程 if (!CreateProcessA ( filePath, // 程序路径 NULL, // 命令行参数 NULL, // 进程句柄不可继承 NULL, // 线程句柄不可继承 FALSE, // 设置句柄继承为 FALSE 0, // 没有创建标志 NULL, // 使用父进程的环境块 NULL, // 使用父进程的起始目录 &si, // 指向 STARTUPINFO 结构 &pi) // 指向 PROCESS_INFORMATION 结构 ) { printf("CreateProcess failed (%d).\n", GetLastError()); return; } // 等待直到子进程退出 WaitForSingleObject(pi.hProcess, INFINITE); // 关闭进程和线程句柄 CloseHandle(pi.hProcess); CloseHandle(pi.hThread);}
int readtxt(const char* filePath){ FILE* file; char line[1024]; int dimension;
int tourSize = 0; int temp;
// 打开文件 file = fopen(filePath, "r"); if (!file) { perror("Failed to open file"); return EXIT_FAILURE; } // 跳过文件头信息 while (fgets(line, sizeof(line), file)) { if (strncmp(line, "DIMENSION : ", 12) == 0) { sscanf(line, "DIMENSION : %d", &dimension); tour = (int*)malloc(sizeof(int) * dimension); if (!tour) { perror("Failed to allocate memory for tour"); fclose(file); return EXIT_FAILURE; } } else if (strncmp(line, "TOUR_SECTION", 12) == 0) { break; // 找到TOUR_SECTION,开始读取路径数据 } }
// 读取路径数据 while (fgets(line, sizeof(line), file)) { if (sscanf(line, "%d", &temp) == 1 && temp != -1) { tour[tourSize++] = temp; } else if (temp == -1) { break; // 读到-1表示路径结束 } }
// 输出读取的路径数据 for (int i = 0; i < tourSize; i++) { printf("%d\n", tour[i]); } fclose(file);}
int main(){ // 加载图片 IMAGE img; loadimage(&img, _T("AA.jpeg"));
H = img.getheight(); W = img.getwidth(); initgraph(W, H, SHOWCONSOLE); _getch(); //setbkcolor(WHITE); //cleardevice(); ColorToGray(&img); GetRGBNUMS(); int TJRGBNUMS[256];
for (int i = 0; i < 256; i++) { TJRGBNUMS[i] = 0; isover[i] = false; } int NUMS = 0; // 写一个获取位置的函数 Point* points = NULL; // 初始化为NULL,表示还没有分配内存 int numPoints = 0; // 当前存储的点的数量 int maxPoints = 100; // 初始分配的点数,可以根据需要调整
srand(time(NULL)); // 初始化随机数生成器
// 分配初始内存 points = (Point*)malloc(maxPoints * sizeof(Point)); if (points == NULL) { fprintf(stderr, "Memory allocation failed\n"); return 1; }
while (true) { int Sand_x; int Sand_y; generateRandomCoordinate(&Sand_x, &Sand_y); // 获取这个地方的X,Y; // 将这个地方的XY带入到影像中,看他的值为多少 int ColorNumber; ColorNumber = GetColorNumber(Sand_x, Sand_y, &img); // 分段赋值 TJRGBNUMS[ColorNumber]++; if (TJRGBNUMS[ColorNumber] < RGBNUMS[ColorNumber]) { if (ColorNumber > 20) { Point newPoint; newPoint.x = Sand_x; // 生成0到100之间的随机数 newPoint.y = Sand_y;
if (!isClose(newPoint, points, numPoints)) { if (numPoints >= maxPoints) { // 如果已经达到最大点数,重新分配内存 maxPoints *= 2; // 可以根据需要调整增长策略 Point* temp = (Point*)realloc(points, maxPoints * sizeof(Point)); if (temp == NULL) { fprintf(stderr, "Memory reallocation failed\n"); free(points); // 释放原有内存 return 1; } points = temp; } points[numPoints++] = newPoint; // 存储新点
putpixel(Sand_x, Sand_y, WHITE); } } } else { isover[ColorNumber] = true; } if (is_over()) { break; } if (NUMS > H * W) { break; } NUMS++; } WriteBat(); WritePar(); int Photo_list_size = numPoints; WriteTsp(points, Photo_list_size); StartProcess("LKH.bat"); readtxt("output.txt"); setlinecolor(WHITE); setlinestyle(PS_SOLID, 1); for (int i = 0; i < numPoints - 1; i++) { line(points[tour[i] - 1].x, points[tour[i] - 1].y, points[tour[i + 1] - 1].x, points[tour[i + 1] - 1].y); Sleep(10); } // 首尾相连 //line(mypoints[tour[100] - 1].X, mypoints[tour[100] - 1].Y, mypoints[tour[0] - 1].X, mypoints[tour[0] - 1].Y); saveimage(_T("a.jpg")); _getch(); closegraph();}

C语言研究
写给自己的笔记,时常写写,时常看看,仅此而已。