卷积后错误的图像结果

Wrong image results after convolution

我正在做一个不使用外部库执行卷积的作业。问题是生成的图像应该是模糊的,但生成的图像是错误的。

这是我正在谈论的图片的link。

程序将灰度 RAW 图像的前 5 个字节作为 header,并将其余部分转换为 2D 向量。每个像素强度 (0-255) 由 unsigned char 中的一个字节表示。二维向量用于卷积,然后将生成的向量写入具有 header.

的新 RAW 图像中
#include <iostream>
#include <string>
#include <fstream>
#include <stdio.h>
#include <vector>
#include <math.h>

using namespace std;

vector<unsigned char> header;
vector < vector<unsigned char> > img;

short int width = 0;
short int height = 0;

void read(string filename, short int &width, short int &height, vector < vector<unsigned char> > &img, vector<unsigned char> &header)
{
    const char* path = (char*)filename.c_str();
    cout << endl << "Opening: " << path << endl << endl;

    ifstream inputfile;
    inputfile.open(path, ios::binary);
    inputfile.seekg(0);

    if (!inputfile)
    {
        inputfile.clear();
        string pathin;
        cout << "File not found "<< endl <<"Enter name of image to open : ";
        getline(cin, pathin);

        read(pathin, width, height, img, header);
    }
    else
    {   //copy header into array
        unsigned char h = 0;
        for (int x = 0; x < 5; x++)
        {

            inputfile >>(h);
            header.push_back(h);
        }
        //extract width and height from array
        width = (header[1]<<8 | header[0]);
        height = (header[3]<<8 | header[2]);
        //copy image data into array
        char a = '0';
        for (int x = 0; x < width; x++)
        {
            vector <unsigned char> row;
            for (int y = 0; y < height; y++)
            {
                inputfile.get(a);
                    row.push_back(a);
                    a = '0';
            }
            img.push_back(row);
        }
        inputfile.close();
    }
    return;
}

void convolution(vector < vector<unsigned char> > &img, short int width, short int height)
{
    vector < vector<int> > mask = { { 1, 1, 1 },
                                    { 1, 1, 1 },
                                    { 1, 1, 1 } };
    int sum;
    double min = 0, max = 0, norm = 0; 

    vector<vector<int>> temp;
    for (int i = 0; i < width; ++i)
    {
        vector<int>temprow;
        for (int j = 0; j < height; ++j)
        {
            temprow.push_back(0);

        }
        temp.push_back(temprow);
    }


    for (int i = 1; i < (width-1); i++)
    {
        for (int j = 1; j < (height-1); j++)
        {
            sum = 0;
            for (int u = -1; u <= 1; u++)
            {
                for (int v = -1; v <= 1; v++)
                {
                    sum = sum + img[i + u][j + v] * (mask[u+1][v+1]);       
                }
            }
            if (sum > max)
                max = sum;
            if (sum < min)
                sum = min;

            temp[i][j] = sum;

        }
    }
    //normalize pixels
    norm = ceil(max / 255);
    cout << "norm = "<<norm<<endl;
    for (int i = 1; i < width; i++)
    {
        for (int j = 1; j < height; j++)
        {
            temp[i][j] = temp[i][j] /norm ;
        }
    }
    //get max and min again
    cout << "max = " << max << endl << "min = " << min<<endl;
    max = 0; min = 0;
    for (int i = 1; i < width-1; i++)
    {
        for (int j = 1; j < height-1; j++)
        {
            if (temp[i][j] > max)
                max = temp[i][j];
            if (temp[i][j] < min)
                min = temp[i][j];
        }
    }
    cout << "max = " << max << endl << "min = " << min << endl;
    //display temp
    cout << "temp: ";
    for (int i = 0; i < 5; i++)
    {
        cout << endl;
        for (int j = 0; j < 5; j++)
        {
            cout << temp[i][j] << "  ";
        }
    }
    //convert int to char
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            img[i][j] = temp[i][j] & 0xff;
        }
    }
    cout << endl;
    //display img 
    cout << "img: ";
    for (int i = 0; i < 5; ++i)
    {
        cout << endl;
        for (int j = 0; j < 5; ++j)
            printf("%d   ", ((unsigned char)(img[i][j])));
    }
    cout << endl << endl;
}

void write(short int width, short int height, vector < vector<unsigned char> > img, vector<unsigned char> header)
{
    string path = "test.raw";
    ofstream output;
    output.open(path, ios::binary);

    for (int i = 0; i < 5; ++i)
    {
        cout << endl;
        for (int j = 0; j < 5; ++j)
            printf("%d   ", ((unsigned char)(img[i][j])));
    }

    if (!output)
    {
        cout << "Error saving file." << endl;
        return;     
    }
    else
    {
        for (int x = 0; x < 5; x++)
            output.put (header[x]);
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
                output.put (img[x][y]);
        }

        output.close();
        cout << "File is saved as: " << path << endl << endl;
    }
}

int main()
{
    string pathin; 

    cout << "Enter name of image to open: ";
    cin >> pathin;

    read(pathin, width, height, img, header);
    convolution(img, width, height);
    write(width, height, img, header);
    system("PAUSE");
    return 0;
}

在这一点上,我确信在写作阶段出了点问题,因为应该有一个黑色边框,但它在图像中丢失了。我可以用 openCV 显示数据,但也无法正常工作。我希望有人能指出导致这个问题的原因。

dropbox link to the image i am working with

这不是一个完整的、已解决的 "do it all for you" 类型的答案,而是一些帮助您入门的一般指南。

一般来说,对于图像处理,您需要能够看到您正在处理的内容,因为您的格式似乎不寻常,如果您可以使用可靠的程序查看您的输入和输出图像,将会有所帮助.

因此,首先,您可以使用 ImageMagick(它安装在大多数 Linux 发行版上,可用于 macOS 和 Windows)将图像转换为 JPEG 或 PNG,如下所示:

在终端中将 5 字节 header 的 512x479 图像转换为 JPEG:

magick -depth 8 -size 512x479+5 gray:cana.raw result.jpg

更好 - 现在您可以轻松、一致地查看结果。


接下来,拥有一些合理的示例图像是个好主意,因此通过从图像中取出前 5 header 个字节并附加一堆(恰好 245,248 个)零(黑色像素)来使用 Perl 生成这些图像):

perl -e 'print `head -c5 cana.raw` . "\x00"x245248' > black.raw

然后也做一个白色的:

perl -e 'print `head -c5 cana.raw` . "\xff"x245248' > white.raw

还有一个mid-grey一个:

perl -e 'print `head -c5 cana.raw` . "\x80"x245248' > grey.raw

因此,尝试应用您的阅读和写作,不对每一个进行任何处理,并确保它们保持不变。然后添加你的卷积并再次测试。


最后,和你的调试器成为朋友。您没有说您使用的是什么编译器,所以我不能在这里非常具体,但请检查您的图像大小是否正确,并且您正在处理正确的行数和每行的正确元素数。


至于您的代码...您忘记将 max 设置为 255。

此外,您似乎使用与存储输入图像相同的数组来存储输出图像。这对于卷积来说效果不是很好,因为每个新像素都取决于其上方(和下方)像素的值,并且如果您已经在上一次循环中处理了上方的行并将输出值在那里......你会造成混乱,一半取决于原始图像,一半取决于新图像 - 这意味着你会得到重影和重复的元素 - 就像你的图像出现一样。尝试为输出图像创建第二个数组。