我的主要编码语言是C++,但我偶尔使用C。我用Union查找算法编写了一些简单的图像分割。请随时评论任何事情!
#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
:
发布于 2021-04-26 16:39:17
由于标准库标题是相互独立的,因此以一致的顺序包含它们是有意义的--字母是一个很好的选择。
这里的main()
函数非常长--不仅仅是一个屏幕。这通常表明,对于不同的职责,应该将其划分为不同的功能。特别是,文件读取似乎值得它自己的可重用功能。
在读取文件时,我们需要更加健壮。如果任何一个fopen()
失败,我们很快就会使用返回的空指针,导致未定义的行为。如果fread()
或fwrite()
失败,那么程序将继续运行,可能会产生毫无意义的结果。
假设文件格式的endianness与主机endianness一致是不明智的,如下所示:
uint32_t宽度=*(uint32_t*)&标头18;uint32_t高度=*(uint32_t*)&标头22;
最好从字节流显式地组合值。
发布于 2021-04-26 14:53:16
在FindCompress
和Union
中,参数N
、i
和j
可以作为const传递,因为它们在函数中没有更改。
在C语言中,union
是一个关键字,尽管函数名目前在CamelCase中,但我仍然建议不要使用它。
typedef struct Segment Segment;
struct Segment {
unsigned char value;
size_t parent;
double average;
size_t size;
};
可以写成:
typedef struct {
unsigned char value;
size_t parent;
double average;
size_t size;
} Segment;
if...else
语句上使用括号,即使它们是一行。以后可能会有更多行添加到if...else语句中。当这种情况发生时,忘记添加括号可能会导致错误,如果从一开始就添加括号,那么这些错误是可以避免的。for/while/if...else
语句周围都是这样做的好地方。将这些点应用于FindCompress
函数的示例:
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;
}
https://codereview.stackexchange.com/questions/260028
复制相似问题