2025-11-08 09:55:15 +08:00
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <queue>
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
// ͼ<><CDBC><EFBFBD>ߴ<EFBFBD>
|
|
|
|
|
|
const int WIDTH = 256;
|
|
|
|
|
|
const int HEIGHT = 256;
|
|
|
|
|
|
|
|
|
|
|
|
// 8<><38><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
const int dx[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
|
|
|
|
|
|
const int dy[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD>ؽṹ
|
|
|
|
|
|
struct Pixel {
|
|
|
|
|
|
int x, y; // <20><><EFBFBD><EFBFBD>
|
|
|
|
|
|
int value; // <20><><EFBFBD><EFBFBD>ֵ(<28>Ҷ<EFBFBD>)
|
|
|
|
|
|
Pixel(int x_, int y_, int v_) : x(x_), y(y_), value(v_) {}
|
|
|
|
|
|
// <20><><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ıȽϺ<C8BD><CFBA><EFBFBD>(С<><D0A1><EFBFBD><EFBFBD>)
|
|
|
|
|
|
bool operator<(const Pixel& other) const {
|
|
|
|
|
|
return value > other.value; // ע<>⣺<EFBFBD><E2A3BA><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD>Ĭ<EFBFBD><C4AC><EFBFBD>Ǵѣ<F3B6A5B6><D1A3><EFBFBD><EFBFBD>ﷴת<EFB7B4>Ƚ<EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡPGM<47><4D>ʽͼ<CABD><CDBC>
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>з<EFBFBD>
|
|
|
|
|
|
file.ignore(1);
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>ֵ
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD>ΪPGMͼ<4D><CDBC>
|
|
|
|
|
|
void writePGM(const string& filename, int labels[HEIGHT][WIDTH], int num_labels) {
|
|
|
|
|
|
ofstream file(filename, ios::binary);
|
|
|
|
|
|
file << "P5\n" << WIDTH << " " << HEIGHT << "\n255\n";
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD>ǩӳ<C7A9>䵽0-255<35><35>Χ
|
|
|
|
|
|
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; // <20><>ˮ<EFBFBD><CBAE><EFBFBD>߽<EFBFBD>
|
|
|
|
|
|
file.write((char*)&val, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ˮ<EFBFBD><CBAE><EFBFBD>㷨ʵ<E3B7A8><CAB5>
|
|
|
|
|
|
int watershed(int image[HEIGHT][WIDTH], int labels[HEIGHT][WIDTH]) {
|
|
|
|
|
|
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>ǩ<EFBFBD><C7A9>-1<><31>ʾδ<CABE><CEB4><EFBFBD>ǣ<EFBFBD>0<EFBFBD><30>ʾ<EFBFBD>߽<EFBFBD>
|
|
|
|
|
|
memset(labels, -1, sizeof(int) * HEIGHT * WIDTH);
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
|
|
|
|
|
|
priority_queue<Pixel> pq;
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>оֲ<D0BE><D6B2><EFBFBD>Сֵ<D0A1><D6B5>Ϊ<EFBFBD><CEAA>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>
|
|
|
|
|
|
int current_label = 0;
|
|
|
|
|
|
bool is_min;
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><D2B5><EFBFBD><EFBFBD>оֲ<D0BE><D6B2><EFBFBD>Сֵ<D0A1><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
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]));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD>
|
|
|
|
|
|
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) {
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD>
|
|
|
|
|
|
if (labels[nx][ny] == -1) {
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>
|
|
|
|
|
|
labels[nx][ny] = labels[x][y];
|
|
|
|
|
|
pq.push(Pixel(nx, ny, image[nx][ny]));
|
|
|
|
|
|
}
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѱ<EFBFBD><D1B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD>߽<EFBFBD>
|
|
|
|
|
|
else if (labels[nx][ny] != labels[x][y]) {
|
|
|
|
|
|
labels[x][y] = -1; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD>߽<EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return current_label;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
<EFBFBD>㷨˵<EFBFBD><EFBFBD>
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD>ˮ<EFBFBD><EFBFBD><EFBFBD>㷨<EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD>α<EFBFBD>ʾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD>ĻҶ<EFBFBD>ֵ<EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>θ߶ȣ<EFBFBD><EFBFBD>Ҷ<EFBFBD>ֵԽ<EFBFBD>ͱ<EFBFBD>ʾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Խ<EFBFBD><EFBFBD>
|
|
|
|
|
|
<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʶ<EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><EFBFBD><EFBFBD><EFBFBD>оֲ<EFBFBD><EFBFBD><EFBFBD>Сֵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Щ<EFBFBD>dz<EFBFBD>ʼ<EFBFBD><EFBFBD> "<EFBFBD><EFBFBD>ˮ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
|
|
|
|
|
|
<EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD>̣<EFBFBD>ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><EFBFBD>У<EFBFBD><EFBFBD><EFBFBD>С<EFBFBD>ѣ<EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD>ӵ͵<EFBFBD><EFBFBD>ߵ<EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD>̣<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><EFBFBD>С<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ÿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
<EFBFBD>߽<EFBFBD>ȷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬ<EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD>ˮ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD>γɷ<EFBFBD>ˮ<EFBFBD><EFBFBD><EFBFBD>߽<EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
ʹ<EFBFBD>÷<EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
<EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD> 256x256 <EFBFBD><EFBFBD> PGM <EFBFBD><EFBFBD>ʽ<EFBFBD>Ҷ<EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊinput.pgm
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>룺g++ watershed.cpp - o watershed
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD>г<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. / watershed
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊoutput.pgm<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD>ͬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ò<EFBFBD>ͬ<EFBFBD>Ҷȱ<EFBFBD>ʾ<EFBFBD><EFBFBD><EFBFBD>߽<EFBFBD>Ϊ<EFBFBD><EFBFBD>ɫ
|
|
|
|
|
|
|
|
|
|
|
|
ע<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD><EFBFBD><EFBFBD>ǻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>汾<EFBFBD><EFBFBD>û<EFBFBD>м<EFBFBD><EFBFBD><EFBFBD>Ԥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>裨<EFBFBD><EFBFBD>ȥ<EFBFBD>룩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܶԸ<EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD>Ч<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
ʵ<EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ȶ<EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٹ<EFBFBD><EFBFBD>ȷָ<EFBFBD>
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD>4 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 8 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><EFBFBD>ָ<EFBFBD>Ч<EFBFBD><EFBFBD>
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD>ɫͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD>Ϊ<EFBFBD>Ҷ<EFBFBD>ͼ<EFBFBD>ٴ<EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߴ<EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>WIDTH<EFBFBD><EFBFBD>HEIGHT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int main() {
|
|
|
|
|
|
int image[HEIGHT][WIDTH];
|
|
|
|
|
|
int labels[HEIGHT][WIDTH];
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC>
|
|
|
|
|
|
if (!readPGM("input.pgm", image)) {
|
|
|
|
|
|
cerr << "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡͼ<EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD>" << endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ִ<>з<EFBFBD>ˮ<EFBFBD><CBAE><EFBFBD>㷨
|
|
|
|
|
|
int num_labels = watershed(image, labels);
|
|
|
|
|
|
cout << "<EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>õ<EFBFBD> " << num_labels << " <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" << endl;
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
writePGM("output.pgm", labels, num_labels);
|
|
|
|
|
|
cout << "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѱ<EFBFBD><EFBFBD><EFBFBD>Ϊ output.pgm" << endl;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <queue>
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
#include "SG_baseDataType.h"
|
|
|
|
|
|
#include "SG_baseAlgo_Export.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
// <20><>ȡPPM<50><4D>ʽͼ<CABD>棩
|
|
|
|
|
|
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; // <20><>֧<EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>PPM
|
|
|
|
|
|
|
|
|
|
|
|
file.ignore(); // <20><><EFBFBD>Ի<EFBFBD><D4BB>з<EFBFBD>
|
|
|
|
|
|
vector<unsigned char> data(img.width * img.height * 3);
|
|
|
|
|
|
file.read((char*)data.data(), data.size());
|
|
|
|
|
|
|
|
|
|
|
|
// תΪ<D7AA>Ҷ<EFBFBD>ͼ<EFBFBD><CDBC>Y = 0.299*R + 0.587*G + 0.114*B<><42>
|
|
|
|
|
|
img.gray.resize(img.height, vector<int>(img.width));
|
|
|
|
|
|
img.markers.resize(img.height, vector<int>(img.width, 0)); // <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼΪ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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD>ΪPPM<50><4D>ʽ
|
|
|
|
|
|
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) { // <20><>ˮ<EFBFBD><CBAE><EFBFBD>߽磨<DFBD><E7A3A8>ɫ<EFBFBD><C9AB>
|
|
|
|
|
|
file.put(255); file.put(0); file.put(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else { // <20><><EFBFBD><EFBFBD><F2A3A8B8>ݱ<EFBFBD><DDB1><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD>ɲ<EFBFBD>ͬ<EFBFBD><CDAC>ɫ<EFBFBD><C9AB>
|
|
|
|
|
|
int color = (img.markers[i][j] * 50) % 256;
|
|
|
|
|
|
file.put(color);
|
|
|
|
|
|
file.put((color + 85) % 256);
|
|
|
|
|
|
file.put((color + 170) % 256);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 8<><38><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB>
|
|
|
|
|
|
const int dx[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
|
|
|
|
|
|
const int dy[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
|
|
|
|
|
|
|
2025-11-09 22:54:23 +08:00
|
|
|
|
const int dx4[] = { -1, 0, 0, 1};
|
|
|
|
|
|
const int dy4[] = { 0, -1, 1, 0};
|
|
|
|
|
|
|
2025-11-08 09:55:15 +08:00
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD>Сֵ<D0A1><D6B5>Ϊ<EFBFBD><CEAA>ʼ<EFBFBD><CABC><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
void findMinima(SWD_waterShedImage& img, int& markerCount) {
|
|
|
|
|
|
markerCount = 1; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>ID
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD>ÿ<EFBFBD><C3BF><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD><D8A3>ж<EFBFBD><D0B6>Ƿ<EFBFBD>Ϊ<EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD>Сֵ<D0A1><D6B5>8<EFBFBD><38><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><D0A1>
|
|
|
|
|
|
for (int i = 1; i < img.height - 1; ++i) {
|
|
|
|
|
|
for (int j = 1; j < img.width - 1; ++j) {
|
|
|
|
|
|
if (1 == img.markers[i][j]) //<2F><><EFBFBD><EFBFBD>
|
|
|
|
|
|
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) {
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ظ<EFBFBD><D8B8><EFBFBD><EFBFBD><EFBFBD>ͬһ<CDAC><D2BB><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>Сֵ
|
|
|
|
|
|
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; // <20><><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ˮ<EFBFBD><CBAE><EFBFBD>㷨<EFBFBD><E3B7A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
2025-11-09 22:54:23 +08:00
|
|
|
|
void watershed(SWD_waterShedImage& img)
|
2025-11-08 09:55:15 +08:00
|
|
|
|
{
|
|
|
|
|
|
int markerCount;
|
|
|
|
|
|
findMinima(img, markerCount); // <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD>Ҷ<EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>ģ<EFBFBD><C4A3>ˮλ<CBAE><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
vector<pair<int, pair<int, int>>> pixels; // (<28>Ҷ<EFBFBD>ֵ, (x,y))
|
|
|
|
|
|
for (int i = 0; i < img.height; ++i) {
|
|
|
|
|
|
for (int j = 0; j < img.width; ++j) {
|
2025-11-09 22:54:23 +08:00
|
|
|
|
pixels.emplace_back(img.gray[i][j], make_pair(j, i));
|
2025-11-08 09:55:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
sort(pixels.begin(), pixels.end());
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>ִ<EFBFBD><D6B4>עˮ<D7A2><CBAE><EFBFBD><EFBFBD>
|
|
|
|
|
|
for (const auto& p : pixels) {
|
|
|
|
|
|
int x = p.second.first;
|
|
|
|
|
|
int y = p.second.second;
|
|
|
|
|
|
if (img.markers[x][y] > 0) continue; // <20>ѱ<EFBFBD><D1B1>ǵ<EFBFBD><C7B5><EFBFBD><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
// <20>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѱ<EFBFBD><D1B1>ǵ<EFBFBD><C7B5><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
vector<int> 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; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3>ݲ<EFBFBD><DDB2><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (neighborMarkers.size() == 1) {
|
|
|
|
|
|
img.markers[x][y] = neighborMarkers[0]; // <20><><EFBFBD><EFBFBD>ͬһ<CDAC><D2BB><EFBFBD><EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>㣬<F2BDBBBB><E3A3AC><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA>ˮ<EFBFBD><CBAE>
|
|
|
|
|
|
img.markers[x][y] = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-09 22:54:23 +08:00
|
|
|
|
//ģ<><C4A3>עˮ<D7A2><CBAE><EFBFBD><EFBFBD>עˮ<D7A2>ڿ<EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>ͬˮλע<CEBB><D7A2>
|
|
|
|
|
|
// inlet: עˮ<D7A2><CBAE>
|
|
|
|
|
|
// level: ˮλ
|
|
|
|
|
|
void waterInjection(SWD_waterShedImage& img, SVzNL2DPoint inlet, int level)
|
|
|
|
|
|
{
|
|
|
|
|
|
int py = inlet.y;
|
|
|
|
|
|
int px = inlet.x;
|
|
|
|
|
|
int marker = img.markers[py][px];
|
|
|
|
|
|
std::vector<SVzNL2DPoint> levelPts;
|
|
|
|
|
|
levelPts.push_back(inlet);
|
|
|
|
|
|
int readPtr = 0;
|
|
|
|
|
|
while (readPtr < levelPts.size())
|
|
|
|
|
|
{
|
|
|
|
|
|
SVzNL2DPoint seed = levelPts[readPtr];
|
|
|
|
|
|
readPtr++;
|
|
|
|
|
|
|
|
|
|
|
|
//4<>ڽ<EFBFBD>
|
|
|
|
|
|
for (int d = 0; d < 4; ++d)
|
|
|
|
|
|
{
|
|
|
|
|
|
int nx = seed.x + dx4[d];
|
|
|
|
|
|
int ny = seed.y + dy4[d];
|
|
|
|
|
|
if (ny >= 0 && ny < img.height && nx >= 0 && nx < img.width)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((img.gray[ny][nx] == level) && (img.markers[ny][nx] == 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
img.markers[ny][nx] = marker;
|
|
|
|
|
|
SVzNL2DPoint nxt_seed = { nx, ny };
|
|
|
|
|
|
levelPts.push_back(nxt_seed);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-08 09:55:15 +08:00
|
|
|
|
}
|
2025-11-09 22:54:23 +08:00
|
|
|
|
}
|
2025-11-08 09:55:15 +08:00
|
|
|
|
|
2025-11-09 22:54:23 +08:00
|
|
|
|
bool _checkMarkerExist(std::vector<int>& MarkerLst, int marker)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool exist = false;
|
|
|
|
|
|
for (int i = 0, i_max = (int)MarkerLst.size(); i < i_max; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (MarkerLst[i] == marker)
|
|
|
|
|
|
{
|
|
|
|
|
|
exist = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return exist;
|
|
|
|
|
|
}
|
2025-11-08 09:55:15 +08:00
|
|
|
|
|
2025-11-09 22:54:23 +08:00
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD><EFBFBD>з<EFBFBD>ˮ<EFBFBD><CBAE><EFBFBD>㷨
|
|
|
|
|
|
// watershedSeeds:<3A><><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
// maxLevel:<3A><><EFBFBD><EFBFBD>ˮλֵ
|
|
|
|
|
|
void wd_seedWatershed(SWD_waterShedImage& img, std::vector<SSG_2DValueI>& watershedSeeds, int maxLevel, int startMakerID)
|
|
|
|
|
|
{
|
|
|
|
|
|
int markerCount = startMakerID;
|
|
|
|
|
|
int seedSize = (int)watershedSeeds.size();
|
|
|
|
|
|
for (int i = 0; i < seedSize; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
int px = watershedSeeds[i].x;
|
|
|
|
|
|
int py = watershedSeeds[i].y;
|
|
|
|
|
|
watershedSeeds[i].value = markerCount;
|
|
|
|
|
|
img.markers[py][px] = markerCount; // <20><><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
int greyValue = img.gray[py][px]; //ˮλ
|
|
|
|
|
|
SVzNL2DPoint inlet = { px, py }; //עˮ<D7A2><CBAE>
|
|
|
|
|
|
waterInjection(img, inlet, greyValue);//עˮ<D7A2><CBAE><EFBFBD><EFBFBD>ͬһˮλ<CBAE><CEBB><EFBFBD>ӵIJ<D3B5><C4B2><EFBFBD>ע<EFBFBD><D7A2>
|
|
|
|
|
|
markerCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD>Ҷ<EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>ģ<EFBFBD><C4A3>ˮλ<CBAE><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
std::vector<std::vector<SVzNL2DPoint>> levelPtList;
|
|
|
|
|
|
levelPtList.resize(maxLevel + 1);
|
|
|
|
|
|
for (int y = 0; y < img.height; ++y)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int x = 0; x < img.width; ++x)
|
|
|
|
|
|
{
|
|
|
|
|
|
int level = img.gray[y][x];
|
|
|
|
|
|
if (level <= maxLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
SVzNL2DPoint a_pt = { x, y };
|
|
|
|
|
|
levelPtList[level].push_back(a_pt);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool allDone = true;
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>ִ<EFBFBD><D6B4>עˮ<D7A2><CBAE><EFBFBD><EFBFBD>
|
|
|
|
|
|
for (int i = 0; i <= maxLevel; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (levelPtList[i].size() == 0)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
//<2F><>ͬһˮλ<CBAE><CEBB>level)ִ<>е<EFBFBD><D0B5><EFBFBD>עˮ
|
|
|
|
|
|
bool doInjection = true;
|
|
|
|
|
|
while (doInjection)
|
|
|
|
|
|
{
|
|
|
|
|
|
int injectionNum = 0;
|
|
|
|
|
|
std::vector<SVzNL2DPoint> resiPts;
|
|
|
|
|
|
int ptSize = (int)levelPtList[i].size();
|
|
|
|
|
|
for (int pi = 0; pi < ptSize; pi++)
|
|
|
|
|
|
{
|
|
|
|
|
|
int x = levelPtList[i][pi].x;
|
|
|
|
|
|
int y = levelPtList[i][pi].y;
|
|
|
|
|
|
if (img.markers[y][x] > 0) continue; // <20>ѱ<EFBFBD><D1B1>ǵ<EFBFBD><C7B5><EFBFBD><EFBFBD>ӵ<EFBFBD>
|
|
|
|
|
|
|
|
|
|
|
|
// <20>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѱ<EFBFBD><D1B1>ǵ<EFBFBD><C7B5><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
vector<int> neighborMarkers;
|
|
|
|
|
|
for (int d = 0; d < 8; ++d)
|
|
|
|
|
|
{
|
|
|
|
|
|
int nx = x + dx[d];
|
|
|
|
|
|
int ny = y + dy[d];
|
|
|
|
|
|
if (ny >= 0 && ny < img.height && nx >= 0 && nx < img.width)
|
|
|
|
|
|
{
|
|
|
|
|
|
int m = img.markers[ny][nx];
|
|
|
|
|
|
if (m > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool exist = _checkMarkerExist(neighborMarkers, m);
|
|
|
|
|
|
if (false == exist)
|
|
|
|
|
|
neighborMarkers.push_back(m);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (neighborMarkers.empty())
|
|
|
|
|
|
{
|
|
|
|
|
|
resiPts.push_back(levelPtList[i][pi]);// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3>ȴ<EFBFBD><C8B4><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (neighborMarkers.size() == 1) {
|
|
|
|
|
|
img.markers[y][x] = neighborMarkers[0]; // <20><><EFBFBD><EFBFBD>ͬһ<CDAC><D2BB><EFBFBD><EFBFBD>
|
|
|
|
|
|
injectionNum++;
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>㣬<F2BDBBBB><E3A3AC><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA>ˮ<EFBFBD><CBAE>
|
|
|
|
|
|
img.markers[y][x] = -1;
|
|
|
|
|
|
injectionNum++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
levelPtList[i].clear();
|
|
|
|
|
|
levelPtList[i].insert(levelPtList[i].end(), resiPts.begin(), resiPts.end());
|
|
|
|
|
|
|
|
|
|
|
|
if ((injectionNum == 0) || (levelPtList[i].size() == 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
doInjection = false;
|
|
|
|
|
|
if (levelPtList[i].size() > 0)
|
|
|
|
|
|
allDone = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (true == allDone)
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-11-08 09:55:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endif
|