Как перевернуть этот градиент PGM на 90 градусов?(хранится в векторах) - PullRequest
0 голосов
/ 20 мая 2019

Я работал над реализацией градиента оттенков серого с помощью различных методов сглаживания, но задача требует, чтобы градиент был горизонтальным, начиная с черного слева. В моих попытках повернуть изображение по горизонтали я попытался:

std::reverse(result.begin(), result.end())

Я также пытался обрабатывать вектор как двумерный массив:

temp = result[i][j];
result[i][j] = result[i][width - 1 - j];
result[i][width - 1 - j] = temp;

Ни один из этих методов до сих пор не работал. Вот код, с которым я работаю:

//***headers n stuff***
vector<vector<int>> gradient(int height, int width)
{
    assert(height > 0 && width > 0);

    int cf = height / 255;
    int color = 0;
    vector<vector<int>> result(width, vector<int>(height));
    for (int i = 0; i < height; i += cf)
    {
        for (int j = 0; j < cf; j++)
        {
            fill(result[i + j].begin(), result[i + j].end(), color % 255);
        }
        color--;
    }
    stable_sort(result.begin(), result.end());
    return result;
}

vector<vector<int>> Ordered(int height, int width, vector<vector<int>> result)
{
    int ditherSize = 3;
    int diterLookup[] = { 8, 3, 4, 6, 1, 2, 7, 5, 9 };

    vector<vector<int>> temp(height, vector<int>(width));
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int xlocal = i%ditherSize;
            int ylocal = j%ditherSize;
            int requiredShade = diterLookup[xlocal + ylocal * 3]*255/9;
            if (requiredShade >= result[i][j])
            {
                result[i][j] = 0;
            }
            else {
                result[i][j] = 255;
            }
        }
    }
    return temp;
}


vector<vector<int>> Random(int height, int width, vector<vector<int>> result)
{
    int ditherSize = 3;
    int diterLookup[] = { 8, 3, 4, 6, 1, 2, 7, 5, 9 };

    //vector<vector<int>> result(height, vector<int>(width));
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {

            int requiredShade = rand() % 255;
            if (requiredShade >= result[i][j]) {
                result[i][j] = 0;
            }
            else {
                result[i][j] = 255;
            }
        }
    }
    return result;
}

vector<vector<int>> Floyd_Steinberg(int height, int width, vector<vector<int>> result)
{
    int ditherSize = 3;
    int diterLookup[] = { 8, 3, 4, 6, 1, 2, 7, 5, 9 };

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int oldpixel = result[i][j];
            int newpixel;
            if (oldpixel<=127) {
                newpixel = 0;
            }
            else {
                newpixel = 255;
            }
            result[i][j] = newpixel;
            int quanterror = oldpixel - newpixel;
            if (j < width - 1) {
                result[i][j+1] += quanterror * 7 / 16;
            }
            if (i < height - 1) {
                if (j > 0){
                    result[i + 1][j - 1] += quanterror * 3 / 16;
                }
                result[i+1][j] += quanterror * 5 / 16;
                if (j < width - 1) {
                    result[i + 1][j + 1] += quanterror * 1 / 16;
                }
            }

        }
    }
    return result;
}


vector<vector<int>> JJN(int height, int width, vector<vector<int>> result)
{
    int ditherSize = 3;
    int diterLookup[] = { 8, 3, 4, 6, 1, 2, 7, 5, 9 };

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int oldpixel = result[i][j];
            int newpixel;
            if (oldpixel <= 127) {
                newpixel = 0;
            }
            else {
                newpixel = 255;
            }
            result[i][j] = newpixel;
            int quanterror = oldpixel - newpixel;
            if (j < width - 1) {
                result[i][j + 1] += quanterror * 7 / 48;
                if(j<width-2)
                    result[i][j + 2] += quanterror * 5 / 48;
            }

            if (i < height - 1) {
                if (j > 0) {
                    if (j > 1)
                        result[i + 1][j - 2] += quanterror * 3 / 48;
                    result[i + 1][j - 1] += quanterror * 5 / 48;
                }

                result[i + 1][j] += quanterror * 7 / 48;
                if (j < width - 1) {
                    result[i + 1][j + 1] += quanterror * 5 / 48;
                    if (j < width - 2)
                        result[i + 1][j + 2] += quanterror * 3 / 48;
                }
            }

            if (i < height - 2) {
                if (j > 0) {
                    if(j>1)
                        result[i + 2][j - 2] += quanterror * 1 / 48;
                    result[i + 2][j - 1] += quanterror * 3 / 48;
                }
                result[i + 2][j] += quanterror * 5 / 48;
                if (j < width - 1) {
                    result[i + 2][j + 1] += quanterror * 3 / 48;
                    if (j < width - 2)
                        result[i + 2][j + 2] += quanterror * 1 / 48;
                }
            }

        }
    }
    return result;
}

int main(int argc, char *argv[])
{
    if (argc < 5) {
        cout << "usage:" << endl << "prog.exe <filename> <width> <height> <dithering>"<<endl;
        return 0;
    }
    stringstream w(argv[2]);
    stringstream h(argv[3]);
    stringstream d(argv[4]);
    int numcols, numrows, dithering;

   //***handling error cases ***

    srand(time(0));
    ofstream file;

    file.open(argv[1]);

    if (!file)
    {
        cout << "can't open file" << endl;
        return 0;
    }

    file << "P5" << "\n";

    file << numrows << " " << numcols << "\n";

    file << 255 << "\n";

    vector<vector<int>> pixmap{ gradient(numrows, numcols) };
    switch (dithering) {
    case 1:
        pixmap = Ordered(numrows, numcols, pixmap);
        break;
    case 2:
        pixmap = Random(numrows, numcols, pixmap);
        break;
    case 3:
        pixmap = Floyd_Steinberg(numrows, numcols, pixmap);
        break;
    case 4:
        pixmap = JJN(numrows, numcols, pixmap);
        break;
    default:
        break;
    }
    for_each(pixmap.begin(), pixmap.end(), [&](const auto& v) {
        copy(v.begin(), v.end(), ostream_iterator<char>{file, ""});
    });

    file.close();
}

А вот и результат Использование упорядоченного дизеринга

1 Ответ

0 голосов
/ 20 мая 2019

Если ваше изображение в серой шкале сохраняется как std::vector<std::vector<int>>, я сделал для вас следующий код.
Он поворачивает изображение на 90 градусов в тригонометрическом направлении:

#include <iostream>
#include <vector>

typedef std::vector<std::vector<int>> GrayScaleImage;

// To check is the GrayScaleImage is valid (rectangular and not empty matrix)
bool isValid(const GrayScaleImage & gsi)
{
    bool valid(true);

    if(!gsi.empty())
    {
        size_t width(gsi[0].size());
        for(unsigned int i = 1; valid && (i < gsi.size()); ++i)
        {
            if(gsi[i].size() != width)
                valid = false;
        }
    }
    else
        valid = false;

    return valid;
}

// To print the GrayScaleImage in the console (for the test)
void display(const GrayScaleImage & gsi)
{
    for(const std::vector<int> & line : gsi)
    {
        for(size_t i = 0; i < line.size(); ++i)
            std::cout << line[i] << ((i < line.size()-1) ? " " : "");
        std::cout << '\n';
    }
    std::cout << std::flush;
}

// To rotate the GrayScaleImage by 90 degrees in the trigonometric direction
bool rotate90(const GrayScaleImage & gsi, GrayScaleImage & result)
{
    bool success(false);

    if(isValid(gsi))
    {
        result = GrayScaleImage(gsi[0].size());

        for(const std::vector<int> & line : gsi)
        {
            for(unsigned int i = 0; i < line.size(); ++i)
                result[gsi[0].size()-1 - i].push_back(line[i]);
        }
        success = true;
    }

    return success;
}

// Test
int main()
{
    GrayScaleImage original { {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11} };

    GrayScaleImage rotated;
    rotate90(original, rotated);

    std::cout << "Original:" << std::endl;
    display(original);

    std::cout << "\nRotated:" << std::endl;
    display(rotated);

    return 0;
}

Функция, которая вас заинтересует: rotate90().

Результат теста, записанного в функции main():

Оригинал:
0 1 2
3 4 5
6 7 8
9 10 11

Повёрнуто:
2 5 8 11
1 4 7 10
0 3 6 9

Как видите, он успешно работал.

Надеюсь, это поможет.


EDIT:

Я пытался создать реальное изображение в градациях серого, и функция rotate90() работала хорошо.
Вот вид до и после поворота изображения (2 примера, пейзаж и портрет):

grayscale image rotation landscape
Пример с пейзажным изображением
grayscale image rotation portrait
Пример с портретным изображением

Итак, теперь мы знаем, что функция работает хорошо.

Я вижу, что ваш результат не такой, как ожидалось (черная область добавлена, несоответствие размеров), такого рода поведение может возникнуть, когда вы ошибаетесь с размерами матриц.


EDIT2:

Недопустимый вывод не из-за rotate90(), а из-за генерации файла PGM. Я думаю, это потому, что данные записываются в виде двоичных файлов, а не заголовка.
Следующая функция, которую я написал, создает допустимые файлы PGM:

typedef std::vector<std::vector<uint8_t>> GrayScaleImage;
bool createPGMImage(const std::string & file_path, const GrayScaleImage & img)
{
    bool success(false);

    if(isValid(img))
    {
        std::ofstream out_s(file_path, std::ofstream::binary);
        if(out_s)
        {
            out_s << "P5\n" << img[0].size() << ' ' << img.size() << '\n' << 255 << '\n';

            for(const std::vector<uint8_t> & line : img)
            {
                for(uint8_t p : line)
                    out_s << p;
                out_s << std::flush;
            }

            success = true;
            out_s.close();
        }
    }

    return success;
}

Функция isValid() та же, что и для rotate90().

.

Я также заменил значения int на значения uint8_t (unsigned char), чтобы сделать их более согласованными, поскольку мы записываем значения в один байт (0-255).

...