Как изменить разрешение картинки RGB в буфере?

312
01 марта 2017, 21:56

Скриншот экрана хранится в буфере в виде байт RGB. Нужно независимо от его исходных размеров привести его к разрешению 1280х720.

Как это сделать на С++ (на выходе тоже должен быть буфер с данными)?

Answer 1

Ну, можно отресайзить любой библиотекой, которая работает с графикой (opencv, ImageMagic, тысячи их). Можно отресайзить своей функцией - билинейная интерполяция пишется достаточно просто. Пример на opencv:

// допустим, картинка хранится в каком-нибудь std::string
std::string image_source = "...";
std::vector<unsigned char> bytes(image_source.data(), image_source.data() + image_source.size());
cv::Mat image = cv::imdecode(bytes, CV_LOAD_IMAGE_COLOR);
cv::resize(image, image, cv::Size(1280, 720));
// обратно в буфер
std::vector<unsigned char> resized;
cv::imencode(".png", image, resized);

Дополняю ответ.
По просьбе в комментарии привожу "ручную" реализацию билинейной интерполяции. Поскольку картинку в любом случае надо чем-то прочитать, я для простоты выбрал для этого libjpg.
Ошибки (для краткости) не обрабатываются.
Функции чтения картинки тоже приводятся, чтобы лучше было понятно, как именно картинка хранится в памяти (3 канала, одномерный массив).

Билинейная интерполяция - в функции im_scale_factor().

#include <jpeglib.h>
struct image_t{
    image_t() : w(0), h(0), pixels(0) {}
    unsigned char *pixels;
    unsigned int w;
    unsigned int h;
};
struct pixel_t {
    unsigned char b;
    unsigned char g;
    unsigned char r;
};
bool read_jpg(const char *path, image_t *image)
{
    FILE *file = fopen(path, "rb");
    struct jpeg_decompress_struct info;
    struct jpeg_error_mgr err;
    info.err = jpeg_std_error( &err );
    jpeg_create_decompress( &info );
    jpeg_stdio_src( &info, file );
    jpeg_read_header( &info, true );
    jpeg_start_decompress( &info );
    int w = info.output_width;
    int h = info.output_height;
    int numChannels = info.num_components; // 3 = RGB, 4 = RGBA
    unsigned long dataSize = w * h * numChannels;
    // read RGB(A) scanlines one at a time into data[]
    unsigned char *data = (unsigned char *)malloc( dataSize );
    unsigned char* rowptr;
    while ( info.output_scanline < h )
    {
        rowptr = data + info.output_scanline * w * numChannels;
        jpeg_read_scanlines( &info, &rowptr, 1 );
    }
    jpeg_finish_decompress( &info );    
    fclose( file );
    image->pixels = data;
    image->h = h;
    image->w = w;
    return true;
}
void write_jpg(const char *path, image_t *image)
{
    struct jpeg_compress_struct cinfo;
    jpeg_create_compress(&cinfo);
    FILE *fp = fopen(path, "w+");
    jpeg_stdio_dest(&cinfo, fp);
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr); 
    cinfo.image_width = image->w;
    cinfo.image_height = image->h;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;
    jpeg_set_defaults(&cinfo);
    jpeg_start_compress(&cinfo, FALSE);
    unsigned char *stride = (unsigned char *)malloc(image->w * 3);
    unsigned char *data = (unsigned char*)image->pixels;
    JSAMPROW row_pointer[1];
    for (int i = 0; i < image->h; ++i) {
        memcpy(stride, data + i * image->w * 3, image->w * 3);
        row_pointer[0] = stride;
       jpeg_write_scanlines(&cinfo, &stride, 1);
    }
   jpeg_finish_compress(&cinfo);
   jpeg_destroy_compress(&cinfo);
   fclose(fp);
}
pixel_t pixel(image_t *im, int h, int w)
{
    pixel_t p;
    p.b = im->pixels[h*im->w*3 + w*3 + 0];
    p.g = im->pixels[h*im->w*3 + w*3 + 1];
    p.r = im->pixels[h*im->w*3 + w*3 + 2];
    return p;
}
void im_scale_factor(image_t *src, image_t *dest, float h_factor, float w_factor)
{
    for (int i = 0; i < dest->h; ++i) {
        float fY = ((float) i) / h_factor;
        int iY = (int) fY;
        for (int j = 0; j < dest->w; ++j) {
            float fX = ((float) j) / w_factor;
            int iX = (int) fX;
            pixel_t q11 = pixel(src, iY, iX);
            pixel_t q12 = pixel(src, iY + 1, iX);
            pixel_t q21 = pixel(src, iY, iX + 1);
            pixel_t q22 = pixel(src, iY + 1, iX + 1);
            unsigned char r1_b = (iX + 1 - fX)/(iX + 1 - iX)*q11.b + (fX - iX)/(iX + 1 - iX)*q21.b;
            unsigned char r1_g = (iX + 1 - fX)/(iX + 1 - iX)*q11.g + (fX - iX)/(iX + 1 - iX)*q21.g;
            unsigned char r1_r = (iX + 1 - fX)/(iX + 1 - iX)*q11.r + (fX - iX)/(iX + 1 - iX)*q21.r;
            unsigned char r2_b = (iX + 1 - fX)/(iX + 1 - iX)*q12.b + (fX - iX)/(iX + 1 - iX)*q22.b;
            unsigned char r2_g = (iX + 1 - fX)/(iX + 1 - iX)*q12.g + (fX - iX)/(iX + 1 - iX)*q22.g;
            unsigned char r2_r = (iX + 1 - fX)/(iX + 1 - iX)*q12.r + (fX - iX)/(iX + 1 - iX)*q22.r;
            unsigned char b = (iY + 1 - fY)/(iY + 1 - iY)*r1_b + (fY - iY)/(iY + 1 - iY)*r2_b;
            unsigned char g = (iY + 1 - fY)/(iY + 1 - iY)*r1_g + (fY - iY)/(iY + 1 - iY)*r2_g;
            unsigned char r = (iY + 1 - fY)/(iY + 1 - iY)*r1_r + (fY - iY)/(iY + 1 - iY)*r2_r;
            dest->pixels[i*dest->w*3 + j*3 + 0] = b;
            dest->pixels[i*dest->w*3 + j*3 + 1] = g;
            dest->pixels[i*dest->w*3 + j*3 + 2] = r;
        }
    }
}
void im_scale(image_t *src, image_t *dest, int h, int w)
{
    std::cerr << "scale to : " << w << " x " << h << "\n";
    dest->h = h;
    dest->w = w;
    dest->pixels = (unsigned char*)malloc(h * w * 3);
    float scale_x = w / (float)src->w;
    float scale_y = h / (float)src->h;
    im_scale_factor(src, dest, scale_y, scale_x);
}
int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0] << " in out" << std::endl;
        return -1;
    }
    image_t image;
    read_jpg(argv[1], &image);
    image_t result;
    im_scale(&image, &result, 1280, 720);
    write_jpg(argv[2], &result);
    return 0;
}

Для использования потребуется libjpg:

g++ main.cpp -ljpeg
READ ALSO
Спецификатор final для функции

Спецификатор final для функции

При добавлении спецификатора final мы запрещаем переопределять метод в базовом классеТогда зачем нам нужен в базовом классе создавать виртуальный...

304
Печать двух страниц на одном листе

Печать двух страниц на одном листе

Как мне в Qt5 настроить так чтобы на одном листе A4 печатать два формата A5?

252
Javascript выпадающий список [требует правки]

Javascript выпадающий список [требует правки]

Как скрывать значение выпадающего спписока

304
Разница ParseFloat и Number [дубликат]

Разница ParseFloat и Number [дубликат]

На данный вопрос уже ответили:

334