-1

Friends! I have a set of small images that I need to lay out as a table in a Tiff file.

How the files should look like in the final file: enter image description here I use the LibTIFF library for this. Tell me how this can be implemented? I implement my solution in C #, but the language does not matter, since rewriting the solution is not a problem.

        var Row = 10;
        var Column = 10;

        var PIXEL_WIDTH = 8810;
        var PIXEL_HEIGHT = 11810;
        //Each small image has a resolution of 881x1181
        using (Tiff tiff = Tiff.Open("big.tif", "w"))
        {
            tiff.SetField(TiffTag.IMAGEWIDTH, PIXEL_WIDTH);
            tiff.SetField(TiffTag.IMAGELENGTH, PIXEL_HEIGHT);
            tiff.SetField(TiffTag.COMPRESSION, Compression.LZW);
            tiff.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);
            tiff.SetField(TiffTag.ORIENTATION, Orientation.TOPLEFT);
            tiff.SetField(TiffTag.ROWSPERSTRIP, PIXEL_HEIGHT);

            tiff.SetField(TiffTag.XRESOLUTION, 120);
            tiff.SetField(TiffTag.YRESOLUTION, 120);

            tiff.SetField(TiffTag.BITSPERSAMPLE, 8);
            tiff.SetField(TiffTag.SAMPLESPERPIXEL, 3);

            tiff.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);

            int tileC = 0;

            for (int row = 0; row < Row; row++)
            {
                for (int col = 0; col < Column; col++)
                {
                    Bitmap bitmap = new Bitmap($"{row}x{col}.png");
                    byte[] raster = getImageRasterBytes(bitmap, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                    tiff.WriteEncodedStrip(tileC++, raster, raster.Length);
                }
            }
            tiff.WriteDirectory();
        }

Thanks in advance!

Maxim_A
  • 183
  • 2
  • 13
  • 1
    What have you tried already? Can you clarify whether you want to use a TIFF-specific feature for packing multiple images *separately* or if you just want one big image with all of the sub-images stitched together? Doing it the second way doesn't have anything to do with the TIFF format at all. – Romen Jul 03 '19 at 20:19
  • I corrected the description. Yes, I need one large image with all the sub-images stitched together. if Tiff is not suitable, then in which direction should I look to solve this problem? – Maxim_A Jul 03 '19 at 20:29
  • https://stackoverflow.com/questions/28676738/create-a-bigtiff-4gb-file-with-bitmiracle-libtiff-net I was based on this topic. I understand that it uses tiles. I thought that this can be done without tiles. – Maxim_A Jul 03 '19 at 20:31
  • I did not mean to say TIFF can not be used for this. I mean to say that your question is about combining images, and the type of file you save the image as is unrelated to solving your problem. The code you posted shows you can make a TIFF file in C#, the hard part is organizing the image data in memory correctly. I will attempt to write an answer for you based on your code. – Romen Jul 03 '19 at 20:33
  • Yes, I have a problem with that. at the end, the file is generated, but it is all either a curve, or repeated pictures in rows and other visual defects, depending on my attempts. – Maxim_A Jul 03 '19 at 20:37

1 Answers1

1

First let's put aside the TIFF part of your question. The major problem is that you need to figure out how to organize the pixel data in memory before you can save the final image to any image type.

I will provide my own simple example to illustrate how that pixel data should be organized.


Let's say we want to combine 9 images in a 3x3 table.
Each image will be 3x3 pixels and 8-bit mono (1 channel).
That makes this example nice and simple with 9 bytes per image, and each image having a stride of 3 bytes per row. The combined image will end up being 9x9 pixels, 81 bytes total.

These images are named A, B, C ... I
A0 is byte 0 of the pixel data for A, A1 is byte 1, and so on...

These images are going to be organized in a 3x3 table like this:

ABC
DEF
GHI

Then the final data layout would need to look like this:

byte[] pixelData = [
    A0,A1,A2,B0,B1,B2,C0,C1,C2,
    A3,A4,A5,B3,B4,B5,C3,C4,C5,
    A6,A7,A8,B6,B7,B8,C6,C7,C8,
    D0,D1,D2,E0,E1,E2,F0,F1,F2,
    D3,D4,D5,E3,E4,E5,F3,F4,F5,
    D6,D7,D8,E6,E7,E8,F6,F7,F8,
    G0,G1,G2,H0,H1,H2,I0,I1,I2,
    G3,G4,G5,H3,H4,H5,I3,I4,I5,
    G6,G7,G8,H6,H7,H8,I6,I7,I8
];

The pixel array above could then be written to any image file you want. Including TIFF.

Notice in the array above:
As you iterate through that array from index 0 to 80, you will be jumping back and forth between the three images that are on the same row until you reach the next row entirely, where the next 3 images from that row are visited in the same pattern.


To achieve a memory layout like this, you can use several approaches.

  1. TIFF files have support for breaking up a large image into equal-sized tiles. This could be used to achieve what you are asking for by writing each image to its own tile using the libTIFF library. There is a limitation that each TIFF tile must have dimensions that are multiples of 16.
  2. The Graphics class in System.Drawing can be used to make one large blank image and then you can draw each sub-image into the large image at any desired position. (This is the easiest way to get what you want, but it can be slow.)
  3. Doing it manually with a loop:
// For the example I have given above, in pseudo C# code

int composite_stride = image_stride * 3; // 3 is the number of images per row
int composite_size = composite_stride * image_height * 3 // 3 is the number of images per column
byte[] composite_pixels = new byte[composite_size];

// Loop over each image you want to combine
// We need some way to know which row/column the image is from, let that be assigned to table_row and table_col
// We are also assuming all images have the same width and height
foreach (image in table)
{
    int comp_x = table_col * image.width;
    int comp_y = table_row * image.height;

    for (int y=0; y<image.height; y++)
    {
        // Calculate the array index that the current row starts at
        int comp_row_start = comp_y * composite_stride;

        for (int x=0; x<image.width; x++)
        {
            // Calculate the array index in the composite image to write to, and the source image index to copy from
            int comp_index = comp_row_start + ((comp_x + x) * image.bytes_per_pixel);
            int img_index = (y * image.stride) + (x * image.bytes_per_pixel);
            composite_pixels[pixel_index] = image.pixels[img_index];
        }
    }
}
Romen
  • 1,617
  • 10
  • 26