#if 0 #include #include #include #include #include #include using namespace std; // 图像尺寸 const int WIDTH = 256; const int HEIGHT = 256; // 8邻域方向 const int dx[] = { -1, -1, -1, 0, 0, 1, 1, 1 }; const int dy[] = { -1, 0, 1, -1, 1, -1, 0, 1 }; // 像素结构 struct Pixel { int x, y; // 坐标 int value; // 像素值(灰度) Pixel(int x_, int y_, int v_) : x(x_), y(y_), value(v_) {} // 优先队列需要的比较函数(小顶堆) bool operator<(const Pixel& other) const { return value > other.value; // 注意:优先队列默认是大顶堆,这里反转比较 } }; // 读取PGM格式图像 bool readPGM(const string& filename, int image[HEIGHT][WIDTH]) { ifstream file(filename, ios::binary); if (!file) return false; string magic; int w, h, max_val; file >> magic >> w >> h >> max_val; if (magic != "P5" || w != WIDTH || h != HEIGHT) return false; // 跳过换行符 file.ignore(1); // 读取像素值 unsigned char pixel; for (int i = 0; i < HEIGHT; ++i) { for (int j = 0; j < WIDTH; ++j) { file.read((char*)&pixel, 1); image[i][j] = (int)pixel; } } return true; } // 保存分割结果为PGM图像 void writePGM(const string& filename, int labels[HEIGHT][WIDTH], int num_labels) { ofstream file(filename, ios::binary); file << "P5\n" << WIDTH << " " << HEIGHT << "\n255\n"; // 将标签映射到0-255范围 int step = 255 / (num_labels + 1); for (int i = 0; i < HEIGHT; ++i) { for (int j = 0; j < WIDTH; ++j) { unsigned char val = (labels[i][j] + 1) * step; if (labels[i][j] == -1) val = 0; // 分水岭边界 file.write((char*)&val, 1); } } } // 分水岭算法实现 int watershed(int image[HEIGHT][WIDTH], int labels[HEIGHT][WIDTH]) { // 初始化标签:-1表示未标记,0表示边界 memset(labels, -1, sizeof(int) * HEIGHT * WIDTH); // 优先队列(按像素值从小到大处理) priority_queue pq; // 标记所有局部最小值作为初始盆地 int current_label = 0; bool is_min; // 第一步:找到所有局部最小值并标记 for (int i = 0; i < HEIGHT; ++i) { for (int j = 0; j < WIDTH; ++j) { is_min = true; for (int d = 0; d < 8; ++d) { int ni = i + dx[d]; int nj = j + dy[d]; if (ni >= 0 && ni < HEIGHT && nj >= 0 && nj < WIDTH) { if (image[ni][nj] < image[i][j]) { is_min = false; break; } } } if (is_min) { labels[i][j] = current_label++; pq.push(Pixel(i, j, image[i][j])); } } } // 第二步:模拟淹没过程 while (!pq.empty()) { Pixel p = pq.top(); pq.pop(); int x = p.x; int y = p.y; for (int d = 0; d < 8; ++d) { int nx = x + dx[d]; int ny = y + dy[d]; if (nx >= 0 && nx < HEIGHT && ny >= 0 && ny < WIDTH) { // 如果邻域像素未标记 if (labels[nx][ny] == -1) { // 标记为当前区域 labels[nx][ny] = labels[x][y]; pq.push(Pixel(nx, ny, image[nx][ny])); } // 如果邻域像素已标记且属于不同区域,则标记为边界 else if (labels[nx][ny] != labels[x][y]) { labels[x][y] = -1; // 当前像素设为边界 } } } } return current_label; } #if 0 算法说明 这个实现遵循分水岭算法的基本原理: 地形表示:将图像的灰度值视为地形高度,灰度值越低表示地势越低 初始盆地:首先识别图像中的所有局部最小值,这些是初始的 "积水盆地" 淹没过程:使用优先队列(最小堆)模拟从低到高的淹没过程,按像素值从小到大处理 区域生长:每个像素被分配给其相邻的最低盆地 边界确定:当两个不同盆地的水相遇时,形成分水岭边界 使用方法 准备一张 256x256 的 PGM 格式灰度图像,命名为input.pgm 编译代码:g++ watershed.cpp - o watershed 运行程序:. / watershed 结果将保存为output.pgm,其中不同区域用不同灰度表示,边界为黑色 注意事项 这个实现是基础版本,没有加入预处理步骤(如去噪),可能对复杂图像分割效果不佳 实际应用中通常需要先对图像进行平滑处理,减少过度分割 可以通过调整邻域处理方式(4 邻域或 8 邻域)来改变分割效果 对于彩色图像,需要先转换为灰度图再处理 如果需要处理更大尺寸的图像,可以修改WIDTH和HEIGHT常量,并相应调整内存分配 int main() { int image[HEIGHT][WIDTH]; int labels[HEIGHT][WIDTH]; // 读取输入图像 if (!readPGM("input.pgm", image)) { cerr << "无法读取图像文件!" << endl; return -1; } // 执行分水岭算法 int num_labels = watershed(image, labels); cout << "分割完成,共得到 " << num_labels << " 个区域" << endl; // 保存结果 writePGM("output.pgm", labels, num_labels); cout << "结果已保存为 output.pgm" << endl; return 0; } #endif #else #include #include #include #include #include #include #include "SG_baseDataType.h" #include "SG_baseAlgo_Export.h" using namespace std; #if 0 // 读取PPM格式图像(简化版) bool readPPM(const string& filename, Image& img) { ifstream file(filename, ios::binary); if (!file) return false; string magic; int maxVal; file >> magic >> img.width >> img.height >> maxVal; if (magic != "P6") return false; // 仅支持二进制PPM file.ignore(); // 忽略换行符 vector data(img.width * img.height * 3); file.read((char*)data.data(), data.size()); // 转为灰度图(Y = 0.299*R + 0.587*G + 0.114*B) img.gray.resize(img.height, vector(img.width)); img.markers.resize(img.height, vector(img.width, 0)); // 初始化标记图为0 for (int i = 0; i < img.height; ++i) { for (int j = 0; j < img.width; ++j) { int idx = (i * img.width + j) * 3; int r = data[idx]; int g = data[idx + 1]; int b = data[idx + 2]; img.gray[i][j] = (int)(0.299 * r + 0.587 * g + 0.114 * b); } } return true; } // 保存分割结果为PPM格式 void saveResult(const string& filename, const Image& img) { ofstream file(filename, ios::binary); file << "P6\n" << img.width << " " << img.height << "\n255\n"; for (int i = 0; i < img.height; ++i) { for (int j = 0; j < img.width; ++j) { if (img.markers[i][j] == -1) { // 分水岭边界(红色) file.put(255); file.put(0); file.put(0); } else { // 区域(根据标记值生成不同颜色) int color = (img.markers[i][j] * 50) % 256; file.put(color); file.put((color + 85) % 256); file.put((color + 170) % 256); } } } } #endif // 8邻域坐标偏移 const int dx[] = { -1, -1, -1, 0, 0, 1, 1, 1 }; const int dy[] = { -1, 0, 1, -1, 1, -1, 0, 1 }; // 计算局部极小值作为初始种子点 void findMinima(SWD_waterShedImage& img, int& markerCount) { markerCount = 1; //背景对应的ID // 遍历每个像素,判断是否为局部极小值(8邻域内最小) for (int i = 1; i < img.height - 1; ++i) { for (int j = 1; j < img.width - 1; ++j) { if (1 == img.markers[i][j]) //背景 continue; bool isMin = true; #if 0 for (int d = 0; d < 8; ++d) { int y = i + dy[d]; int x = j + dx[d]; if (img.gray[y][x] < img.gray[i][j]) { isMin = false; break; } } #else for (int dy = -7; dy < 7; dy++) { for (int dx = -7; dx < 7; dx++) { int y = i + dy; int x = j + dx; if ((x >= 0) && (x < img.width) && (y >= 0) && (y < img.height)) { if (img.gray[y][x] < img.gray[i][j]) { isMin = false; break; } } } } #endif if (isMin) { // 避免重复标记同一区域的极小值 bool hasMarker = false; for (int d = 0; d < 8; ++d) { int y = i + dy[d]; int x = j + dx[d]; if (img.markers[y][x] > 1) { hasMarker = true; img.markers[y][x] = img.markers[i][j]; break; } } if (!hasMarker) { img.markers[i][j] = ++markerCount; // 新种子点 } } } } } // 分水岭算法主函数 void watershed(SWD_waterShedImage& img) { int markerCount; findMinima(img, markerCount); // 自动标记种子点 // 按灰度值排序所有像素(模拟水位上升) vector>> pixels; // (灰度值, (x,y)) for (int i = 0; i < img.height; ++i) { for (int j = 0; j < img.width; ++j) { pixels.emplace_back(img.gray[i][j], make_pair(i, j)); } } sort(pixels.begin(), pixels.end()); // 遍历排序后的像素,执行注水过程 for (const auto& p : pixels) { int x = p.second.first; int y = p.second.second; if (img.markers[x][y] > 0) continue; // 已标记的种子点 // 收集邻域已标记的区域 vector neighborMarkers; for (int d = 0; d < 8; ++d) { int nx = x + dx[d]; int ny = y + dy[d]; if (nx >= 0 && nx < img.height && ny >= 0 && ny < img.width) { int m = img.markers[nx][ny]; if (m > 0) { neighborMarkers.push_back(m); } } } if (neighborMarkers.empty()) { continue; // 无邻域标记,暂不处理 } else if (neighborMarkers.size() == 1) { img.markers[x][y] = neighborMarkers[0]; // 属于同一区域 } else { // 多区域交汇,标记为分水岭 img.markers[x][y] = -1; } } } #if 0 int main() { Image img; if (!readPPM("input.ppm", img)) { // 输入PPM图像 cerr << "无法读取图像!" << endl; return -1; } watershed(img); // 执行分水岭分割 saveResult("watershed_result.ppm", img); // 保存结果 cout << "分割完成,结果已保存为 watershed_result.ppm" << endl; return 0; } #endif #endif