C++ 图像过滤算法

C++ Image filtering algorithm

本文关键字:算法 过滤 图像 C++      更新时间:2023-10-16

我正忙于尝试实现一种图像过滤算法,其工作原理如下: 过滤器是大小为 N 的二维数组(N 必须是奇数),因此具有 N*N 个元素。大小为 3 的示例筛选器为:

0.25 1 0.25
0.25 0 0.25
0.25 1 0.25

对于图像数据中的每个无符号字符(像素),将筛选器数组的中心放在当前工作像素处。然后,对于滤镜覆盖在图像中的每个像素,找到滤镜覆盖的所有像素的加权总和(即每个滤镜值乘以它当前覆盖的像素),并将当前工作图像像素值设置为该加权总和。对图像中的每个像素执行此操作。如果滤镜像素超出图像 2D 阵列的范围(即左、右、上、下),则它必须环绕图像的适当边缘。

所以我有以下代码:

Image Image::operator%(const Filter & g) {
Image filtered = *this;
std::vector<std::vector<float>> filter = g.get_filter();
Image::iterator beg = filtered.begin();
Image::iterator end = filtered.end();
unsigned char* pixel_data = filtered.data.get();
int pixel_index = 0;
while(beg != end) {
// current working pixel value
unsigned char* c_pixel = *beg;
float weight = 0;
// starting x and y position (top left) relative to the centre
// of the filter at index 'pixel'
int start_y = pixel_index - (g.get_size()-1) / 2;
int start_x = pixel_index - (g.get_size()-1) / 2;
for(int row = 0; row < g.get_size(); ++row) {
std::vector<float> r = filter.at(row);
int c_row = start_y + row;
if(c_row >= height) {
c_row %= height;
} else if(c_row < 0) {
c_row += height;
}
for(int col = 0; col < g.get_size(); ++col) {
// current column of filter relative
// to the image pixel
int c_col = start_x + col;
if(c_col >= width) {
c_col %= width;
} else if(c_col < 0) {
c_col += width;
}
weight += pixel_data[this->index(c_col, c_row)]*r.at(col);
}
}
*c_pixel = weight;
++beg;
++pixel_index;
}
return filtered;
}

如果您想知道,this->index(c_col, c_row)将一维数组视为二维数组:

int Image::index(int x, int y) {
return width*y + x;
}

。并且图像数据受std::unique_ptr<unsigned char[]>保护。 这段代码给了我一些奇怪的输出。生成的图像具有不同像素颜色的垂直条纹,有点类似于原始图像颜色。我不知道我做错了什么,因为这种方法在纸上检查,而不是在代码中检查。如果需要,我很乐意添加任何额外的信息。:)

我首先关心的是图像像素格式。 你说输出是std::unique_ptr<unsigned char[]>,但权重是使用浮点数计算和写入的。index方法返回一个索引,而不考虑像素数据大小 { 1_BYTE (单色)、3_BYTE (RGB8)、4_Byte(RGBA8) }。pixel_data是字符(字节),所以我不确定您是否正确索引像素数据,不考虑像素大小,也不忽略alpha(如果需要)。

另一个问题是,如果您使用的是 RGB(a) 数据,那么从 INT->Float 转换将无法正确缩放。 乘以浮点数会将像素缩放为实数,而不是单独的通道。 这将导致通道相互溢出,并且通常不正确。

下一步是创建一个筛选器,该筛选器将数据读取和写入为 RGB 通道的像素(忽略 alpha),以确保筛选器是直通的。 然后,您将编写一个过滤器,通过将 RGB 通道设置为 0 或 255 来删除该通道。(红色通道、蓝色通道、绿色通道)

一旦您确定可以单独正确地操作 RGB,您就可以开始应用权重了。

第一次尝试会很慢。最终你会发现你可以使用MASKs来抓取R_B与G通道分开的通道,你不必担心溢出。 这个魔法大致看起来像这样:

UInt32 WeightPixel(UInt32 value, float weight)
{
const UInt32 MASK1 = (UInt32)0x00ff00ff; // _R_B
const UInt32 MASK2 = (UInt32)0xff00ff00; // A_G_
int f2 = (int)(256 * weight); // Scale weight into char range
// >> 8 is just divide by 256
return (UInt32)(((((value & MASK1) * f2)) >> 8) & MASK1)
| (UInt32)(((((value & MASK2) * f2)) >> 8) & MASK2);
}