Как программно объединить несколько JPEG в более крупный JPEG без потерь (.net) - PullRequest
2 голосов
/ 13 сентября 2010

У меня есть несколько изображений JPEG, и я хочу объединить их в одно большое изображение JPEG.

Я могу сделать это, создав Bitmap, а затем объединить их там, но таким образом, если я сохраню его снова какJPEG изображение будет ухудшаться.

Итак, есть ли какой-нибудь метод, который я могу использовать, чтобы сделать это без потери качества при декодировании / кодировании?

В программе ACDSee я видел возможность вращать JPEG безпотеря качества, поэтому может быть способ объединить несколько изображений без потери качества.

спасибо

Ответы [ 3 ]

5 голосов
/ 13 сентября 2010

Согласно Википедии / JPEG это может быть возможно, если ваши изображения имеют размеры, кратные 16.

Википедия / Редактирование без потерь / JPEG также говорит о JPEGJoin, который может объединять несколько изображений.

В .NET Framework ничего не встроено, но вы можете использовать вышеуказанные инструменты из C #.

3 голосов
/ 14 сентября 2010

Почти все инструменты JPEG без потерь основаны на jpegtran из http://sylvana.net/jpegcrop/jpegtran/ (исходный код доступен).

Вам нужно расширить холст jpeg, а затем использовать все еще экспериментальную «каплю»функциональность для вставки изображения в другое изображение.

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

1 голос
/ 17 сентября 2010

хорошо, я написал код, и поэтому я хотел бы поделиться им здесь:)
обратите внимание, что код не будет работать во всех ситуациях, но это хорошо для моего использования.
Я использую библиотеку LibJpeg.Nethttp://bitmiracle.com/libjpeg.
также есть ошибка в библиотеке (или ошибка во мне :)), из-за которой вы не можете получить компонент height_in_blocks, поэтому этот код будет работать только на квадратных плитках.
iЯ думаю, что изображения должны иметь ту же таблицу квантования, что и упомянутый Властой.
Я думаю, что этот код можно расширить, чтобы поддержать это, но ну, мне не нужна такая поддержка.
, и теперь вот код :)

public void CreateBigImage()
{
    const int iTileWidth = 256;
    const int iTileHeigth = 256;
    int iImageWidthInTiles = 2;
    int iImageHeigthInTiles = 2;

    //Open Image to read its header in the new image
    BitMiracle.LibJpeg.Classic.jpeg_decompress_struct objJpegDecompressHeader = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct();
    System.IO.FileStream objFileStreamHeaderImage = new System.IO.FileStream(GetImagePath(0), System.IO.FileMode.Open, System.IO.FileAccess.Read);
    objJpegDecompressHeader.jpeg_stdio_src(objFileStreamHeaderImage);
    objJpegDecompressHeader.jpeg_read_header(true);

    BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] varrJBlockBigImage = new BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[10];
    for (int i = 0; i < 3; i++)//3 compounds per image (YCbCr)
    {
        int iComponentWidthInBlocks = objJpegDecompressHeader.Comp_info[i].Width_in_blocks;
        int iComponentHeigthInBlocks = iComponentWidthInBlocks;//there is no Height_in_blocks in the library so will use widht for heigth also (wont work if image is not rectangular)
        varrJBlockBigImage[i] = BitMiracle.LibJpeg.Classic.jpeg_common_struct.CreateBlocksArray(iComponentWidthInBlocks * iImageWidthInTiles, iComponentHeigthInBlocks * iImageHeigthInTiles);
    }

    for (int iX = 0; iX < iImageWidthInTiles; iX++)
    {
        for (int iY = 0; iY < iImageHeigthInTiles; iY++)
        {
            WriteImageToJBlockArr(varrJBlockBigImage, GetImagePath(iY*iImageHeigthInTiles+iX), iX, iY);
        }
    }

    System.IO.FileStream objFileStreamMegaMap = System.IO.File.Create(GetImagePath(999));
    BitMiracle.LibJpeg.Classic.jpeg_compress_struct objJpegCompress = new BitMiracle.LibJpeg.Classic.jpeg_compress_struct();
    objJpegCompress.jpeg_stdio_dest(objFileStreamMegaMap);
    objJpegDecompressHeader.jpeg_copy_critical_parameters(objJpegCompress);//will copy the critical parameter from the header image
    objJpegCompress.Image_height = iTileHeigth * iImageHeigthInTiles;
    objJpegCompress.Image_width = iTileWidth * iImageWidthInTiles;
    objJpegCompress.jpeg_write_coefficients(varrJBlockBigImage);
    objJpegCompress.jpeg_finish_compress();
    objFileStreamMegaMap.Close();

    objJpegDecompressHeader.jpeg_abort_decompress();
    objFileStreamHeaderImage.Close();
}

public void WriteImageToJBlockArr(BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] varrJBlockNew, string strImagePath, int iTileX, int iTileY)
{
    BitMiracle.LibJpeg.Classic.jpeg_decompress_struct objJpegDecompress = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct();
    System.IO.FileStream objFileStreamImage = new System.IO.FileStream(strImagePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
    objJpegDecompress.jpeg_stdio_src(objFileStreamImage);
    objJpegDecompress.jpeg_read_header(true);
    BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] varrJBlockOrg = objJpegDecompress.jpeg_read_coefficients();
    for (int i = 0; i < 3; i++)//3 compounds per image (YCbCr)
    {
        int iComponentWidthInBlocks = objJpegDecompress.Comp_info[i].Width_in_blocks;
        int iComponentHeigthInBlocks = iComponentWidthInBlocks;//there is no Height_in_blocks in the library so will use widht for heigth also (wont work if image is not rectangular)
        for (int iY = 0; iY < iComponentHeigthInBlocks; iY++)
        {
            for (int iX = 0; iX < iComponentWidthInBlocks; iX++)
            {
                varrJBlockNew[i].Access(iY + iTileY * iComponentHeigthInBlocks, 1)[0][iX + iTileX * iComponentWidthInBlocks] = varrJBlockOrg[i].Access(iY, 1)[0][iX];
            }
        }
    }
    objJpegDecompress.jpeg_finish_decompress();
    objFileStreamImage.Close();
}
...