首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C:图像分割

C:图像分割
EN

Code Review用户
提问于 2021-04-26 13:04:27
回答 2查看 580关注 0票数 5

我的主要编码语言是C++,但我偶尔使用C。我用Union查找算法编写了一些简单的图像分割。请随时评论任何事情!

代码语言:javascript
运行
复制
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <string.h>

static size_t const MERGE_CRITERION = 50;

typedef struct Segment Segment;

struct Segment {
    unsigned char value;
    size_t parent;
    double average;
    size_t size;
};

size_t FindCompress(size_t N, Segment pixels[static N], size_t i) {
    if (i >= N) return SIZE_MAX;
    if (pixels[i].parent != i) {
        pixels[i].parent = FindCompress(N, pixels, pixels[i].parent);
    }
    return pixels[i].parent;
}

bool Union(size_t N, Segment pixels[static N], size_t i, size_t j) {
    if (i >= N || j >= N) return false;
    size_t root_i = FindCompress(N, pixels, i);
    size_t root_j = FindCompress(N, pixels, j);
    if (root_i == root_j) return false;
    if (fabs(pixels[root_i].average - pixels[root_j].average) > MERGE_CRITERION) {
        return false;
    }
    if (pixels[root_i].size < pixels[root_j].size) {
        size_t temp = root_i;
        root_i = root_j;
        root_j = temp;
    }
    pixels[root_j].parent = root_i;
    double new_average = ((pixels[root_i].average * pixels[root_i].size)
            + (pixels[root_j].average * pixels[root_j].size)) / (double)(pixels[root_i].size + pixels[root_j].size);
    pixels[root_i].average = new_average;
    pixels[root_i].size += pixels[root_j].size;
    return true;
}

int main() {
    FILE* img = fopen("sample.bmp", "rb");
    FILE* outimg = fopen("sample_gray.bmp", "wb");

    unsigned char header[54] = {0};
    fread(header, sizeof(unsigned char), 54, img);
    fwrite(header, sizeof(unsigned char), 54, outimg);

    uint32_t width = *(uint32_t*)&header[18];
    uint32_t height = *(uint32_t*)&header[22];
    uint32_t stride = (width * 3 + 3u) & ~(3u);
    uint32_t padding = stride - width * 3;

    printf("width %u, height %u, stride %u, padding %u\n", width, height, stride, padding);

    size_t image_size = width * height;
    Segment greyscaled[image_size];

    unsigned char pixel[3] = {0};
    for (uint32_t i = 0; i < height; i++) {
        for (uint32_t j = 0; j < width; j++) {
            fread(pixel, 3, 1, img);
            unsigned char gray = (unsigned char)(pixel[0] * 0.3 + pixel[1] * 0.58 + pixel[2] * 0.11);
            greyscaled[i * width + j] = (Segment){.value = gray, .parent = (i * width + j),
                                                  .average = gray, .size = 1};
        }
        fread(pixel, padding, 1, img);
    }
    size_t change_count = 1;

    size_t iteration = 0;
    while (change_count > 0) {
        change_count = 0;
        for (uint32_t j = 1; j < width; j++) {
            if (Union(image_size, greyscaled, j, j - 1)) {
                change_count++;
            }
        }
        for (uint32_t i = 1; i < height; i++) {
            if (Union(image_size, greyscaled, i * width, (i - 1) * width)) {
                change_count++;
            }
            for (uint32_t j = 1; j < width; j++) {
                if (Union(image_size, greyscaled, i * width + j, (i - 1) * width + j)) {
                    change_count++;
                }
                if (Union(image_size, greyscaled, i * width + j, i * width + j - 1)) {
                    change_count++;
                }
            }
        }
        printf("Iteration %zu, change_count %zu\n", ++iteration, change_count);
    }

    for (uint32_t i = 0; i < height; i++) {
        for (uint32_t j = 0; j < width; j++) {
            size_t root = FindCompress(image_size, greyscaled, i * width + j);
            unsigned char gray = (unsigned char) greyscaled[root].average;
            memset(pixel, gray, sizeof(pixel));
            fwrite(&pixel, 3, 1, outimg);
        }
        fwrite(pixel, padding, 1, outimg);
    }

    fclose(img);
    fclose(outimg);

    return EXIT_SUCCESS;
}

原始示例图像:

使用MERGE_CRITERION = 10

使用MERGE_CRITERION = 20

EN

回答 2

Code Review用户

发布于 2021-04-26 16:39:17

由于标准库标题是相互独立的,因此以一致的顺序包含它们是有意义的--字母是一个很好的选择。

这里的main()函数非常长--不仅仅是一个屏幕。这通常表明,对于不同的职责,应该将其划分为不同的功能。特别是,文件读取似乎值得它自己的可重用功能。

在读取文件时,我们需要更加健壮。如果任何一个fopen()失败,我们很快就会使用返回的空指针,导致未定义的行为。如果fread()fwrite()失败,那么程序将继续运行,可能会产生毫无意义的结果。

假设文件格式的endianness与主机endianness一致是不明智的,如下所示:

uint32_t宽度=*(uint32_t*)&标头18;uint32_t高度=*(uint32_t*)&标头22;

最好从字节流显式地组合值。

票数 3
EN

Code Review用户

发布于 2021-04-26 14:53:16

使用const

FindCompressUnion中,参数Nij可以作为const传递,因为它们在函数中没有更改。

避免使用关键字作为函数名.

在C语言中,union是一个关键字,尽管函数名目前在CamelCase中,但我仍然建议不要使用它。

造型

  • 段结构定义
代码语言:javascript
运行
复制
typedef struct Segment Segment;

struct Segment {
    unsigned char value;
    size_t parent;
    double average;
    size_t size;
};

可以写成:

代码语言:javascript
运行
复制
typedef struct {
    unsigned char value;
    size_t parent;
    double average;
    size_t size;
} Segment;
  • 函数和变量的CamelCase在C中是不常见的,通常首选snake_case,但是这当然是一个自以为是的项目。
  • if...else语句上使用括号,即使它们是一行。以后可能会有更多行添加到if...else语句中。当这种情况发生时,忘记添加括号可能会导致错误,如果从一开始就添加括号,那么这些错误是可以避免的。
  • 利用空行来提高可读性。在返回语句之前和for/while/if...else语句周围都是这样做的好地方。

将这些点应用于FindCompress函数的示例:

代码语言:javascript
运行
复制
size_t find_compress(const size_t n, Segment pixels[static n], const size_t i) {
    if (i >= n) { return SIZE_MAX; }

    if (pixels[i].parent != i) {
        pixels[i].parent = find_compress(n, pixels, pixels[i].parent);
    }

    return pixels[i].parent;
}
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/260028

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档