Compressing PNG images using PIL - png

I am having problem with increasing opacity to the image. My original image is of 230 KB
after i resize the image using the code:
Method 1: imh=imgg.resize((1000,500),Image.ANTIALIAS) #filesize is 558 KB
Method 2: imh=imgg.resize((1000,500),Image.ANTIALIAS)
im2 = imh.convert('P', palette=Image.ADAPTIVE) #filesize is 170KB
Now i am adding transparency of the image by using this code:
def reduce_opacity(im, opacity,pathname):
assert opacity >= 0 and opacity <= 1
if im.mode != 'RGBA':
im = im.convert('RGBA')
else:
im = im.copy()
alpha = im.split()[3]
alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
im.putalpha(alpha)
jj=<pathname>
im.save(jj)
return im
Method 1: filesize is 598 KB
Method 2: filesize is 383 KB
So the best code i got till now is
imh=imgg.resize((1000,500),Image.ANTIALIAS)
im2 = imh.convert('P', palette=Image.ADAPTIVE)
reduce_opacity(im2,0.5,name)
which gives me a file size of 383KB. To add opacity it has to be opened in RGBA mode which increase the file size from 170 KB to 383 KB.I am not satisfied with this, i need to reduce the size more, it there any way that i can achieve that, not compromising the quality to a great extent?

Related

PIL: How do I efficiently convert a gray image into black white

I want to have only black or white pixels in a resulting image. I tried the contrast and other filter methods w/o success.
I ended up with the code (pixel access) below which works for me but is slow. I don't know how to implement the code with getdata() and putdata():
for x in range(width):
for y in range(height):
pix = imgcic.getpixel((x, y))
if pix < 200:
pix = 0
else:
pix = 255
imgcic.putpixel((x,y), pix)
This is the original picture. It has some artefacts on the right top
This is the resulting picture w/o artefacts after deciding black/white based on a brightness threshold

PIL opens only first chanel of TIF image

I'm trying to open (and then process) a 3-channel Tif image (8-bits) created with ImageJ.
im = Image.open('spinal.tif')
im.show()
shows me a png for the first channel
n = np.array(im)
print(n.shape)
gives me (400, 450), thus considers only the first channel
How could I work on the different channels? Many thanks
Info on my tif file from ImageJ:
Title: spinal.tif
Width: 1986.4042 microns (450)
Height: 1765.6926 microns (400)
Size: 527K
Resolution: 0.2265 pixels per micron
Voxel size: 4.4142x4.4142x1 micron^3
ID: -466
Bits per pixel: 8 (grayscale LUT)
Display ranges
1: 0-255
2: 0-255
3: 0-255
Image: 1/3 (c:1/3 - 64_spinal_20x lame2.ndpis #1)
Channels: 3
Composite mode: "grayscale"
The file is temporarily available here :
https://filesender.renater.fr/?s=download&token=ab39ca56-24c3-4993-ae78-19ac5cf916ee
I finally found a way around using the scikit-image library.
This opens correctly the 3 channels (matplotlib didn't, nor PIL).
Once I have the array, I can go back to PIL using Image.fromarray to resume the processing.
from skimage.io import imread
from PIL import Image
img = imread('spinal.tif')
im_pil = Image.fromarray(img)
im_pil.show()
print(np.array(im_pil).shape)
This shows the composite image, and the correct (400, 450, 3) shape.
I can then get the different channels with Image.getchannel(channel) as in :
im_BF = im_pil.getchannel(0)
im_BF.show()
Thank you to the contributors who tried to solve my issue (I saw that the file was downloaded several times) and there might be a better way to process these multiple-channels TIF images with PIL, but this looks like working !
Your image is not a 3-channel RGB image. Rather, it is 3 separate images, each one a single greyscale channel. You can see that with ImageMagick:
magick identify spinal.tif
spinal.tif[0] TIFF 450x400 450x400+0+0 8-bit Grayscale Gray 545338B 0.000u 0:00.000
spinal.tif[1] TIFF 450x400 450x400+0+0 8-bit Grayscale Gray 0.000u 0:00.000
spinal.tif[2] TIFF 450x400 450x400+0+0 8-bit Grayscale Gray 0.000u 0:00.000
Or with tiffinfo which comes with libtiff:
TIFF Directory at offset 0x8 (8)
Subfile Type: (0 = 0x0)
Image Width: 450 Image Length: 400
Resolution: 0.22654, 0.22654 (unitless)
Bits/Sample: 8
Compression Scheme: None
Photometric Interpretation: min-is-black
Samples/Pixel: 1
Rows/Strip: 400
Planar Configuration: single image plane
ImageDescription: ImageJ=1.53f
images=3
channels=3
mode=grayscale
unit=micron
loop=false
TIFF Directory at offset 0x545014 (850f6)
Subfile Type: (0 = 0x0)
Image Width: 450 Image Length: 400
Resolution: 0.22654, 0.22654 (unitless)
Bits/Sample: 8
Compression Scheme: None
Photometric Interpretation: min-is-black
Samples/Pixel: 1
Rows/Strip: 400
Planar Configuration: single image plane
ImageDescription: ImageJ=1.53f
images=3
channels=3
mode=grayscale
unit=micron
loop=false
TIFF Directory at offset 0x545176 (85198)
Subfile Type: (0 = 0x0)
Image Width: 450 Image Length: 400
Resolution: 0.22654, 0.22654 (unitless)
Bits/Sample: 8
Compression Scheme: None
Photometric Interpretation: min-is-black
Samples/Pixel: 1
Rows/Strip: 400
Planar Configuration: single image plane
ImageDescription: ImageJ=1.53f
images=3
channels=3
mode=grayscale
unit=micron
loop=false
If it is meant to be 3-channel RGB, rather than 3 separate greyscale channels, you need to save it differently in ImageJ. I cannot advise on that.
If you want combine the 3 channels into a single image on the command-line, you can do that with ImageMagick:
magick spinal.tif -combine spinal-RGB.png
If you want to read it with PIL/Pillow, you need to treat it as an image sequence:
from PIL import Image, ImageSequence
with Image.open("spinal.tif") as im:
for frame in ImageSequence.Iterator(im):
print(frame)
which gives this:
<PIL.TiffImagePlugin.TiffImageFile image mode=L size=450x400 at 0x11DB64220>
<PIL.TiffImagePlugin.TiffImageFile image mode=L size=450x400 at 0x11DB64220>
<PIL.TiffImagePlugin.TiffImageFile image mode=L size=450x400 at 0x11DB64220>
Or, if you want to assemble into RGB, something more like this:
from PIL import Image
# Open image and hunt down separate channels
with Image.open("spinal.tif") as im:
R = im.copy()
im.seek(1)
G = im.copy()
im.seek(2)
B = im.copy()
# Merge the three separate channels into single RGB image
RGB = Image.merge("RGB", (R, G, B))
RGB.save('result.png')

Matlab tifflib code explination

I find that importing in large multi-tiff files through the imread function is slow for what I need to do. I used googlefoo and located a tutorial explaining a faster way to load tif files. I tried to use the code (below) but it keeps crashing halfway through. As the tutorial stated, might have to change the code based on what I'm trying to load, and this is where I am stuck. I do not know enough about the tifflib to try and change the code.
Would anyone be able to help explain the code so I know where to start with processing the data.
InfoImage=imfinfo('file.tif');
mImage=InfoImage(1).Width;
nImage=InfoImage(1).Height;
NumberImages=length(InfoImage);
FinalImage=zeros(nImage,mImage,NumberImages,'uint16');
FileID = tifflib('open',loaderpath,'r');
rps = tifflib('getField',FileID,Tiff.TagID.RowsPerStrip);
for i=1:NumberImages
tifflib('setDirectory',FileID,i);
% Go through each strip of data.
rps = min(rps,nImage);
for r = 1:nImage
row_inds = r:min(nImage,r+rps-1);
stripNum = tifflib('computeStrip',FileID,r);
FinalImage(row_inds,:,i) = tifflib('readEncodedStrip',FileID,stripNum);
end
end
tifflib('close',FileID);
Tiff file format
Filename 'img_stack.tif'
FileModDate '25-Oct-2017 09:31:40'
FileSize 49960190
Format 'tif'
FormatVersion []
Width 1280
Height 720
BitDepth 8
ColorType 'grayscale'
FormatSignature [73,73,42,0]
ByteOrder 'little-endian'
NewSubFileType 0
BitsPerSample 8
Compression 'PackBits'
PhotometricInterpretation 'BlackIsZero'
StripOffsets 1x66 double
SamplesPerPixel 1
RowsPerStrip 11
StripByteCounts 1x66 double
XResolution 72
YResolution 72
ResolutionUnit 'Inch'
Colormap []
PlanarConfiguration 'Chunky'
TileWidth []
TileLength []
TileOffsets []
TileByteCounts []
Orientation 1
FillOrder 1
GrayResponseUnit 0.0100000000000000
MaxSampleValue 255
MinSampleValue 0
Thresholding 1
Offset 19840

Image Compression using imfinfo function in Matlab

I am trying to calculate the compression ratio of a given image. My matlab code is as follows:
temp = imfinfo('flowers.jpg');
comperssion_ratio = (temp.Width * temp.Height * temp.BitDepth) / temp.FileSize;
The imfinfo displays the following:
FileSize: 11569
Format: 'jpg'
FormatVersion: ''
Width: 430
Height: 430
BitDepth: 8
ColorType: 'grayscale'
FormatSignature: ''
NumberOfSamples: 1
CodingMethod: 'Huffman'
CodingProcess: 'Sequential'
Comment: {}
Running the above code gives me a compression ratio of about 120 which is huge and does not seem right. Is there something that I'm doing wrong? I went through a document from MIT and they showed that the Width and Height and BitDepth should be divided by 8 and then divided by the FileSize. Why divide by 8?
The division by factor of 8 is to convert bits to bytes.
According to the Matlab documentation for imfinfo
the FileSize parameter is the size of the compressed file, in bytes.
The compression ratio is defined as:
uncompressed size of image in bytes/compressed size of file in bytes
imfinfo gives you the pixel width, height, and bits per pixel (bit depth). From that you can compute the uncompressed size in bits, and divide by 8 to get bytes.
For the uncompressed image , you have 430*430*8/8 = 184,900 bytes.
The size of the compressed image is 11569 bytes.
So the compression ratio is actually 184,900/11569 or 15.98, not an unreasonable value for JPEG.

Matlab code to generate images from entropy

Could you please help me with this question:
Assume that on average in binary images, 75% of the pixels are white, and 25% are black. What is the entropy of this source? Model this source in Matlab and generate some sample images according to this process
To find the entropy, you just need to apply the definition:
H = -0.25 * log2(0.25) - 0.75 * log2(0.75)
Since we are using log2, the result will be in bits.
As for generating a Matlab B&W (i.e. binary) image of size 512x512, you can simply do:
im = rand(512) < 0.75;
By convention, true = 1 = white and false = 0 = black.