前言
在上一篇文章的处理中,我发现可以将一张复杂的图像,转换为简单的点。我又突发奇想,能不能将转换的点用TIN三角网的方式连接起来。
也就是说,只是将我以前写的代码进行组装拼接,就可以实现我想要的效果,不过在转换中,我又发现,将转换的小点,用彩色的大点表示,又是一种图像处理的表现形式。
原图
处理过程中发现的彩色点图
最终TIN图
源码
///////////////////////////////////////////////////
// 程序名称:TIN算法生成的图像处理
// 编译环境:Mictosoft Visual Studio 2022, EasyX_20200315(beta)
// 作 者:luoyh <2864292458@qq.com>(V:louyh1207)
// 公 众 号:C语言研究
// 最后修改:2024-7-12
//
int NUMBER; // 随机点的个数
int* X = NULL; // 储存 X 方向产生的随机数
int* Y = NULL; // 储存 Y 方向产生的随机数
bool* LineXY = NULL;
//判断这两个点是否连线
bool IsLinked(int p1, int p2)
{
if (p1 >= p2)
return LineXY[(1 + p1) * p1 / 2 + p2];
else
return LineXY[(1 + p2) * p2 / 2 + p1];
}
//储存已经绘制过的线
void Link(int p1, int p2)
{
if (p1 >= p2)
LineXY[(1 + p1) * p1 / 2 + p2] = true;
else
LineXY[(1 + p2) * p2 / 2 + p1] = true;
}
// 绘制随机点
void drawpoint(int x, int y, float z)
{
float S = 1, L = 0.5;
setfillcolor(HSLtoRGB(z, S, L));
solidcircle(x, y, 0);
}
// 用于计算两点的距离
double distance(double x1, double y1, int x2, int y2)
{
return sqrt((double)((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
}
// 用于产生随机点
void Randompoint(int width, int height, int* lx, int* ly, int n)
{
int i;
bool T;
while (true)
{
i = 0;
*lx = (rand() % (width - 60) + 30);
*ly = (rand() % (height - 60) + 30);
while (true)
{
if (i >= n)
{
T = true;
break;
}
if (distance(*lx, *ly, X[i], Y[i]) < 20) // 用于控制随机点之间的距离
{
T = false;
break;
}
i++;
}
if (T == false)
continue;
else
break;
}
}
// 根据三个点坐标算出他们三个形成外接圆的圆心和半径
double CircleCenter(int x1, int y1, int x2, int y2, int x3, int y3, double* x, double* y, double* r)
{
if ((2.0 * (x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2)) == 0)
{
*x = 0;
*y = 0;
*r = 0;
return *r;
}
*x = ((x1 * x1 + y1 * y1) * (y2 - y3) + (x2 * x2 + y2 * y2) * (y3 - y1) + (x3 * x3 + y3 * y3) * (y1 - y2)) / (2.0 * (x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2));
*y = ((x1 * x1 + y1 * y1) * (x3 - x2) + (x2 * x2 + y2 * y2) * (x1 - x3) + (x3 * x3 + y3 * y3) * (x2 - x1)) / (2.0 * (x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2));
*r = sqrt((*x - x1) * (*x - x1) + (*y - y1) * (*y - y1));
return *r;
}
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 main()
{
// 加载图片
IMAGE img;
loadimage(&img, _T("C.jpg"));
H = img.getheight();
W = img.getwidth();
initgraph(W, H, SHOWCONSOLE);
_getch();
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++;
}
putimage(0, 0, &img);
int* Z = (int*)malloc(numPoints * sizeof(int)); // 分配内存
float* G = (float*)malloc(numPoints * sizeof(int));
double max = W * H * W; // 用窗口的长和宽来定义一个相对大的数
int lx; // 储存临时 X 方向产生的变量
int ly; // 储存临时 Y 方向产生的变量
int li; // 用于储存临时判断过的点的下标数
X = (int*)malloc(numPoints * sizeof(int));
Y = (int*)malloc(numPoints * sizeof(int));
LineXY = (bool*)malloc((1 + numPoints) * numPoints / 2 * sizeof(int));
for (int i = 0; i < (1 + numPoints) * numPoints / 2; i++)
{
LineXY[i] = false;
}
srand((unsigned)time(NULL)); // 初始化随机数
for (int i = 0; i < numPoints; i++)
{
//Randompoint(WIDTH, HEIGHT, &lx, &ly, i);
X[i] = points[i].x; // 产生 X 方向的随机数
Y[i] = points[i].y; // 产生 Y 方向的随机数
G[i] = float(rand() % 240); // 产生不同的颜色的随机数(后期可以用来表示高程)
drawpoint(X[i], Y[i], G[i]); // 绘制随机点
}
saveimage(_T("v.jpg"));
float H = 60, S = 1, L = 0.5;
setlinecolor(HSLtoRGB(H, S, L));
// 在随机生成的点中依次判断,找出距离最短的两个点
for (int i = 0; i < numPoints; i++)
{
for (int j = i + 1; j < numPoints; j++)
{
if (max > distance(X[i], Y[i], X[j], Y[j]))
{
lx = i;
ly = j;
max = distance(X[i], Y[i], X[j], Y[j]);
}
}
}
line(X[lx], Y[lx], X[ly], Y[ly]);
Link(lx, ly);
Z[0] = lx;
Z[1] = ly;
int n = 2;
while (true)
{
if (n >= numPoints)
break;
int m = 0;
double rad, Xd, Yd, Rd;
bool OK = false;
max = W * H * W;
// 开始判断随机生成的每一个点
for (int i = 0; i < numPoints; i++)
{
m = 0;
OK = false;
// 判断这个点是否已经判断过,如果已经判断过,返回判断下一个点,如果不在,继续程序
while (true)
{
if (m >= n)
{
m = 0;
break;
}
if (i == Z[m])
{
OK = true;
break;
}
m++;
}
if (OK == true)
continue;
// 在已经确定的两个点和未确定的点进行计算它们形成三角形的的半径,并判断形成的圆内有无其它的点
// 若无其它的点,则可以连线,如有其它的点,则进行判断下一个点
for (int j = 0; j < n; j++)
{
for (int k = 0; k < n; k++)
{
rad = CircleCenter(X[Z[j]], Y[Z[j]], X[Z[k]], Y[Z[k]], X[i], Y[i], &Xd, &Yd, &Rd);
int cc = 0;
OK = false;
while (true)
{
if (cc >= numPoints)
break;
// 判断圆内有无其它点,并且这个被判断的点不能为形成这个圆的这个三个点,如果有其它点,就跳出该循环
if (distance(Xd, Yd, X[cc], Y[cc]) <= Rd && cc != Z[k] && cc != Z[j] && cc != i)
{
OK = true;
break;
}
cc++;
}
// 因为圆内有其它点,结束本次循环
if (OK == true)
continue;
if (max >= rad && rad != 0) // 在三个点围成圆内没有点找到半径最小的
{
lx = Z[j];
ly = Z[k];
if (rad >= W)
continue;
else
{
if (IsLinked(i, lx) == false) // 绘制线段,首先判断这个线段是否已经绘制过
{
Link(i, lx);
if (distance(X[i], Y[i], X[lx], Y[lx]) < 50)
line(X[i], Y[i], X[lx], Y[lx]);
/*Sleep(100);*/ // 如果需要查看绘制过程,去掉注释
}
if (IsLinked(i, ly) == false)
{
Link(i, ly);
if (distance(X[i], Y[i], X[ly], Y[ly]) < 50)
line(X[i], Y[i], X[ly], Y[ly]);
/*Sleep(100);*/ // 如果需要查看绘制过程,去掉注释
}
if (IsLinked(lx, ly) == false)
{
Link(lx, ly);
if (distance(X[lx], Y[lx], X[ly], Y[ly]) < 50)
line(X[lx], Y[lx], X[ly], Y[ly]);
/*Sleep(100);*/ // 如果需要查看绘制过程,去掉注释
}
li = i;
}
}
}
}
}
Z[n] = li;
n++;
}
//重新描点
//for (int i = 0; i < numPoints; i++)
//{
// drawpoint(X[i], Y[i], G[i]);
//}
saveimage(_T("a.jpg"));
_getch();
closegraph();
}