|
|
要实现只提取最外层轮廓(不包含内部孔洞),可以使用连通组件标记算法。以下是完整的解决方案:
方法一:使用边界标记算法
- #include <vector>
- #include <queue>
- using namespace std;
- void extractExternalContours(vector<unsigned char>& image, int width, int height) {
- vector<unsigned char> result(width * height, 0);
- vector<bool> visited(width * height, false);
-
- // 从图像边界开始扫描,标记所有与边界相连的背景
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- // 只处理边界像素
- if (x == 0 || x == width-1 || y == 0 || y == height-1) {
- int idx = y * width + x;
- if (image[idx] == 0 && !visited[idx]) { // 背景像素
- // BFS标记所有相连的背景
- queue<pair<int, int>> q;
- q.push({x, y});
- visited[idx] = true;
-
- while (!q.empty()) {
- auto [cx, cy] = q.front();
- q.pop();
-
- int directions[4][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}};
- for (auto [dx, dy] : directions) {
- int nx = cx + dx, ny = cy + dy;
- if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
- int nidx = ny * width + nx;
- if (!visited[nidx] && image[nidx] == 0) {
- visited[nidx] = true;
- q.push({nx, ny});
- }
- }
- }
- }
- }
- }
- }
- }
-
- // 提取外部轮廓:前景像素且至少有一个邻居是外部背景
- for (int y = 1; y < height-1; y++) {
- for (int x = 1; x < width-1; x++) {
- int idx = y * width + x;
- if (image[idx] == 255) { // 前景像素
- // 检查4邻域是否有外部背景
- bool isContour = false;
- int neighbors[4][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}};
- for (auto [dx, dy] : neighbors) {
- int nx = x + dx, ny = y + dy;
- int nidx = ny * width + nx;
- if (visited[nidx] && image[nidx] == 0) {
- isContour = true;
- break;
- }
- }
-
- if (isContour) {
- result[idx] = 255;
- }
- }
- }
- }
-
- // 将结果复制回原数组
- image = result;
- }
复制代码
方法二:简化版本(适合大多数情况)
- void extractOuterContourSimple(vector<unsigned char>& img, int w, int h) {
- vector<unsigned char> contour(w * h, 0);
-
- for (int y = 1; y < h-1; y++) {
- for (int x = 1; x < w-1; x++) {
- int idx = y * w + x;
- if (img[idx] == 255) { // 前景
- // 检查是否与图像边界背景相连
- bool touchesBorderBg = false;
-
- // 检查4方向连接性
- if (x == 1 && img[idx-1] == 0) touchesBorderBg = true;
- if (x == w-2 && img[idx+1] == 0) touchesBorderBg = true;
- if (y == 1 && img[idx-w] == 0) touchesBorderBg = true;
- if (y == h-2 && img[idx+w] == 0) touchesBorderBg = true;
-
- // 检查4邻域背景连通性
- if (!touchesBorderBg) {
- int dirs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
- for (auto [dx, dy] : dirs) {
- int nx = x + dx, ny = y + dy;
- int nidx = ny * w + nx;
- if (img[nidx] == 0) {
- // 检查这个背景是否能连接到边界
- vector<bool> checked(w * h, false);
- queue<pair<int, int>> q;
- q.push({nx, ny});
- checked[nidx] = true;
-
- while (!q.empty()) {
- auto [cx, cy] = q.front();
- q.pop();
-
- // 如果到达边界,则说明是外部轮廓
- if (cx == 0 || cx == w-1 || cy == 0 || cy == h-1) {
- touchesBorderBg = true;
- break;
- }
-
- int ndirs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
- for (auto [ddx, ddy] : ndirs) {
- int nnx = cx + ddx, nny = cy + ddy;
- int nnidx = nny * w + nnx;
- if (nnx >= 0 && nnx < w && nny >= 0 && nny < h &&
- !checked[nnidx] && img[nnidx] == 0) {
- checked[nnidx] = true;
- q.push({nnx, nny});
- }
- }
- }
- if (touchesBorderBg) break;
- }
- }
- }
-
- if (touchesBorderBg) {
- contour[idx] = 255;
- }
- }
- }
- }
-
- img = contour;
- }
复制代码
使用方法:
- int main() {
- int width = 800; // 你的图像宽度
- int height = 600; // 你的图像高度
- int nPixels = width * height;
-
- std::vector<unsigned char> intermediateBuffer(nPixels);
- // ... 从TIFF加载数据到intermediateBuffer ...
-
- // 提取外部轮廓
- extractExternalContours(intermediateBuffer, width, height);
-
- // 现在intermediateBuffer中只包含最外层轮廓
- return 0;
- }
复制代码
这两个算法都能确保只提取最外层轮廓:
- 方法一通过标记所有与图像边界相连的背景区域
- 方法二通过检查每个前景像素的背景邻居是否能连接到图像边界
这样就能完美实现类似OpenCV的RETR_EXTERNAL功能,且不包含任何内部孔洞轮廓。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 |
|