Why are these red and green squares showing up on my PNG? - png

I have a VTF file that looks like this inside VTFEdit:
I tried to convert it to a PNG in Python using the code below:
import texture2ddecoder, numpy, cv2
from PIL import Image
img_width = 64
img_height = 64
encoded_binary = open('bracketsTest.vtf','rb').read()
#decompressing dxt5 (compression used for this VTF file) to get actual pixel colors, returns BGRA bytes
decoded_binary = texture2ddecoder.decode_bc5(encoded_binary, img_width, img_height)
#creating RGBA png, converting from BGRA (no support for BRGA in PIL it seems)
dec_img = Image.frombytes("RGBA", (img_width, img_height), decoded_binary, 'raw', ("BGRA"))
dec_img.show()
dec_img.save('testpng.png')
And the resulting image came out like this:
As the resulting image does not look the same as it does in VTFEdit, obviously something went wrong. I suspected that it was an issue with the color channel going from BGRA (VTFs are BRGA by default + texture2ddecoder produces BRGA bytes when decompressing) to RGBA, so I tried the following code to convert the image from RGBA to BRGA:
# trying to convert png back to BGRA
image = cv2.imread('testpng.png')
image_bgra = cv2.cvtColor(image, cv2.COLOR_RGBA2BGRA)
cv2.imshow('image',image_bgra)
But the resulting image came out basically the same as before the conversion only with blue squares instead of red ones. What's going on here and how can I fix it? Is there a name for these odd squares?

DXT5 is actually known as Block Compression 3 (BC3). In my case, I incorrectly assumed BC5 = DXT5, so the decompression was wrong (see this wikipedia article for a better explanation). I changed the line decoded_binary = texture2ddecoder.decode_bc5(encoded_binary, img_width, img_height) to decoded_binary = texture2ddecoder.decode_bc3(encoded_binary, img_width, img_height) and the resulting image looked like this:
There are still some odd squares at the top, but deleting/ignoring the header info and low-res thumbnail data seems to fix it:
Know your decompression algorithms!!!

Related

Matlab `imread` is reading in some images as all-zeros

I am using Matlab's imread to read in images, but about half get read in as all-zeros, even though they are not all-black images (I can view them fine in Finder).
The images that fail vary in their:
file extension (PNG, JPG)
colorspace (RGB, Gray)
color profile (sRGB IEC61966-2.1, Calibrated RGB Colorspace, Generic Gray Gamma 2.2 Profile)
However, I have success reading in other PNG and JPG images in RGB and Gray colorspaces. I don't have any instances of successful reads of an sRGB IEC61966-2.1 color profile, although again, not all images that fail have this profile. I can't see any pattern of file extension, colorspace, etc. that distinguishes that fail from the ones that are read in successfully.
I have tried the following:
[img, map, alpha] = imread('fname.png');. In all cases this produces all-zero matrices for img, map, and alpha.
making the file extension explicit, e.g. imread('fname.png', 'png');. The result is the same.
I am running Matlab 2019b on macOS Catalina.
Any suggestions for what might be causing some images to fail and for how to import them successfully?
The images you linked contain an alpha transparency channel so simply reading using imread() will not return the image data. You need to read the image using additiona parameters as defined on the help page:
[imRGB, map, alpha] = imread('AcbK5pRoi.png');
where the imRGB will contain the RGB image and the Alpha will contain the transparency data.
You can use the imRGB variable as a normal image.

PIL simple image paste - image changing color

I'm trying to paste an image onto another, using:
original = Img.open('original.gif')
tile_img = Img.open('tile_image.jpg')
area = 0, 0, 300, 300
original.paste(tile_img, area)
new_cropped.show()
This works except the pasted image changes color to grey.
Image before:
Image after:
Is there a simple way to retain the same pasted image color? I've tried reading the other questions and the documentation, but I can't find any explanation of how to do this.
Many thanks
I believe all GIF images are palettised - that is, rather than containing an RGB triplet at each location, they contain an index into a palette of RGB triplets. This saves space and improves download speed - at the expense of only allowing 256 unique colours per image.
If you want to treat a GIF (or palettised PNG file) as RGB, you need to ensure you convert it to RGB on opening, otherwise you will be working with palette indices rather than RGB triplets.
Try changing the first line to:
original = Img.open('original.gif').convert('RGB')

Converting PIL image to VIPS image

I'm working on some large histological images using Vips image library. Together with the image I have an array with coordinates. I want to make a binary mask which masks out the part of the image within the polygon created by the coordinates. I first tried to do this using vips draw function, but this is very inefficiently and takes forever (in my real code the images are about 100000 x 100000px and the array of polygons are very large).
I then tried creating the binary mask using PIL, and this works great. My problem is to convert the PIL image into an vips image. They both have to be vips images to be able to use the multiply-command. I also want to write and read from memory, as I believe this is faster than writing to disk.
In the im_PIL.save(memory_area,'TIFF') command I have to specify and image format, but since I'm creating a new image, I'm not sure what to put here.
The Vips.Image.new_from_memory(..) command returns: TypeError: constructor returned NULL
from gi.overrides import Vips
from PIL import Image, ImageDraw
import io
# Load the image into a Vips-image
im_vips = Vips.Image.new_from_file('images/image.tif')
# Coordinates for my mask
polygon_array = [(368, 116), (247, 174), (329, 222), (475, 129), (368, 116)]
# Making a new PIL image of only 1's
im_PIL = Image.new('L', (im_vips.width, im_vips.height), 1)
# Draw polygon to the PIL image filling the polygon area with 0's
ImageDraw.Draw(im_PIL).polygon(polygon_array, outline=1, fill=0)
# Write the PIL image to memory ??
memory_area = io.BytesIO()
im_PIL.save(memory_area,'TIFF')
memory_area.seek(0)
# Read the PIL image from memory into a Vips-image
im_mask_from_memory = Vips.Image.new_from_memory(memory_area.getvalue(), im_vips.width, im_vips.height, im_vips.bands, im_vips.format)
# Close the memory buffer ?
memory_area.close()
# Apply the mask with the image
im_finished = im_vips.multiply(im_mask_from_memory)
# Save image
im_finished.tiffsave('mask.tif')
You are saving from PIL in TIFF format, but then using the vips new_from_memory constructor, which is expecting a simple C array of pixel values.
The easiest fix is to use new_from_buffer instead, which will load an image in some format, sniffing the format from the string. Change the middle part of your program like this:
# Write the PIL image to memory in TIFF format
memory_area = io.BytesIO()
im_PIL.save(memory_area,'TIFF')
image_str = memory_area.getvalue()
# Read the PIL image from memory into a Vips-image
im_mask_from_memory = Vips.Image.new_from_buffer(image_str, "")
And it should work.
The vips multiply operation on two 8-bit uchar images will make a 16-bit uchar image, which will look very dark, since the numeric range will be 0 - 255. You could either cast it back to uchar again (append .cast("uchar") to the multiply line) before saving, or use 255 instead of 1 for your PIL mask.
You can also move the image from PIL to VIPS as a simple array of bytes. It might be slightly faster.
You're right, the draw operations in vips don't work well with very large images in Python. It's not hard to write a thing in vips to make a mask image of any size from a set of points (just combine lots of && and < with the usual winding rule), but using PIL is certainly simpler.
You could also consider having your poly mask as an SVG image. libvips can load very large SVG images efficiently (it renders sections on demand), so you just magnify it up to whatever size you need for your raster images.

PIL: converting an image with mode "I" to "RGB" results in a fully white image

The image at the end of this question is a PNG with mode I, which stands for Indexed, as far as I can tell.
I'm trying to create a thumbnail out of it, and save it as JPG with PIL.
However, is I leave the mode alone, PIL won't let me resize it with error unable to generate thumbnail: cannot write mode I as JPEG.
If I convert it to RGB, the result will be a fully white image.
Is there a way to fix this?
https://www.dropbox.com/s/2d1edk2iu4ixk25/NGC281.png
The input image is a 16-bit grayscale PNG, and it appears PIL has a problem with this. Manually converting it to an 8-bit image before further processing makes it work again.
The problem may originate inside PIL itself. The PyPNG homepage asserts
..PIL only has internal representations (PIL mode) for 1-bit and 8-bit channel values. This makes me wonder if PIL can read PNG files with bit depth 2 or 4 (greyscale or palette), and also bit depth 16 (which PNG supports for greyscale and RGB images).
Then again, that page is from 2009. It could be worth tracking down where PIL is maintained from, and report this as a bug (? Or possibly a feature request?).

LodePng 24bit RGB mode can't understand how to choose

I have an YUV 420 (144x176) file from which I read first frame and converted its YUV components to RGB array int rgb[HEIGHT*WIDTH*3]; in which I store R1G1B1...RnGnBn and have an std::vector<unsigned char> image; image.resize(width * height * 4);. My question is:
When I use unsigned error = lodepng::encode(filename, image, width, height); it processes without errors and generates a PNG file, but this file is not even looks like an original image, I think that it uses RGBA while I only have RGB, how to fix it?
P.S. Don't know is this ^ information is enough, so tell me if no, please.
Okay, this question should be closed now.
All I've done is added 255 as Alpha in 4th position, something like this:
if (i%3==0 && i != 0)
image[k++] = 255;
image[k] = rgb[i];
P.S. Another important thing I missed is to put pixels at their real position when reading from YUV file, I mean like shown on Wiki's figure. Note this if you will have problems with YUV.