Start with the first image, and copy it to the proposed output.
For each additional image, figure out how it overlaps the current proposed output image. If the overlap is not complete (the additional image would stick off the edge), then pad the proposed output with -inf so that the new proposed output is large enough to include the additional image. Now take the (possibly newly padded) proposed output, and max() the appropriate existing section of it with the additional image, and store that back over the proposed output.
Keep doing this for each additional image.
At the end, you will have a proposed output that is large enough to include all of the additional images, and that at each point has the maximum of all of the images that overlapped that spot.
The proposed output will probably also have spots that are -inf, which are spots needed to maintain a rectangular array, but which no image got drawn on top of. If you did the minimal padding each time, then you cannot trim those off of the boundary.
You need to decide what to do with those -inf -- whether to set them to black, or set them to some background color, or if to create an alpha channel showing which spots are significant and transparent at the -inf, and then set the array locations to 0.
Now convert the output to the appropriate data type (probably uint8) -- you had to have been working in floating point to use -inf .
And write it out, including any transparency map that might have been created.