Image Compression using imfinfo function in Matlab - 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.

Related

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')

Why is this supported CGBitmapContextCreate parameter combination rejected as unsupported?

I have the following Swift code to make a CGBitmapContext
let imageDirectoryPath:String = "/Users/blah/blah/"
let imageFileName:String = "flower.tif"
let imageNS = NSImage(contentsOfFile: imageDirectoryPath + imageFileName)!
let imageCG = imageNS.CGImageForProposedRect(nil, context: nil, hints: nil)
var rawDataIn:[UInt8] = [UInt8](count: Int(imageNS.size.width) * Int(imageNS.size.height) * 4, repeatedValue: 0xff)
let context = CGBitmapContextCreate(
&rawDataIn,
Int(imageNS.size.width),
Int(imageNS.size.height),
8,
Int(imageNS.size.width * 4),
CGColorSpaceCreateDeviceRGB(),
CGImageAlphaInfo.PremultipliedLast.rawValue)
I get an error from this, and using Xcode's scheme for the project to set the environment variable CGBITMAP_CONTEXT_LOG_ERRORS I get detail about the error:
Nov 10 10:12:16 SwiftConsoleGrabcut[826] :
CGBitmapContextCreate: unsupported parameter combination:
8 integer bits/component;
32 bits/pixel;
RGB color space model; kCGImageAlphaPremultipliedLast;
19789 bytes/row.
Valid parameters for RGB color space model are:
16 bits per pixel, 5 bits per component, kCGImageAlphaNoneSkipFirst
32 bits per pixel, 8 bits per component, kCGImageAlphaNoneSkipFirst
32 bits per pixel, 8 bits per component, kCGImageAlphaNoneSkipLast
32 bits per pixel, 8 bits per component, kCGImageAlphaPremultipliedFirst
32 bits per pixel, 8 bits per component, kCGImageAlphaPremultipliedLast
64 bits per pixel, 16 bits per component, kCGImageAlphaPremultipliedLast
64 bits per pixel, 16 bits per component, kCGImageAlphaNoneSkipLast
128 bits per pixel, 32 bits per component, kCGImageAlphaNoneSkipLast |kCGBitmapFloatComponents
128 bits per pixel, 32 bits per component, kCGImageAlphaPremultipliedLast |kCGBitmapFloatComponents
See Quartz 2D Programming Guide (available online) for more information.
But the parameter combination I use, 32 bits per pixel, 8 bits per component, and kCGImageAlphaPremultipliedLast is one of the supported combinations listed.
Why is it rejected?
This may be a question about size.width. The tif image I am loading into imageNS is 481 pixels wide, but imageNS.size.width is reported as 4947.4285714285716 which may explain the bonkers value given in the error message for bytes per row.

ASTC Texture compression in metal– what should I use as the bytes per row?

I'm writing a program that's using compressed textures in Metal. I'm having a bit of trouble with the replaceRegion() function of MTLTexture. The parameter bytesPerRow just doesn't seem to make sense. It says that for compressed textures, "bytesPerRow is the number of bytes from the beginning of one row of blocks to the beginning of the next."
Now I'm using ASTC with 4x4 blocks, which means that I have 8 bpp. So then 4*4 is 16, and 8 bits is one byte. So I'm guessing that each block size is 16 bytes. But yet, when I enter 16, I get a failed assertion that requires the minimum value to be 4096. What's going on?
Thank you very much.
bytesPerRow = texelsPerRow / blockFootprint.x * 16
uint32_t bytes_per_row(uint32_t texture_width, uint32_t block_width) {
return (texture_width % block_width ? texture_width + (texture_width % block_width) : texture_width) / block_width * 16;
}
This rounds up the texture width to a multiple of the block width first. E.g. a 1024x1024 texture encoded with a block size of 6x6 corresponds to 2736 bytes per row. 1026 / 6 * 16 == 2736.

Compressing PNG images using PIL

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?

Distored image when convert RGB into YUV. How to fix???

From .ppm image, I extracted R,G,B data and saved following structures:
RGB RGB RGB RGB RGB RGB RGB..........
corresponding to each pixel position.
Then I used formula to convert R,G,V into Y,U,V.With each pixel, I obtained YUV correspondingly.
R0G0B0->Y0U0V0 , R1G1B1 ->Y1U1B1, ........
I saved data following YUV422 Data Format:The YUV422 data format shares U and V values between two pixels. As a result, these values are transmitted to the PC image buffer only once for every two pixels.
R0G0B0R1G1B1->Y0UY1V
a,How to calculate U and V from U0,U1 and V0,V1????????
b,In my case , I used this formula:
U=(U0+U1)/2; V=(V0+V1)/2;
Obtained data was saved following structure to create .yuv file:
YUYV YUYV YUYV YUYV YUYV YUYV......
But when I used YUV tools to read new .yuv file, that image is not similar to original image. What did I do wrong here ????
The formula what you have employed is the right one. But a small correction in the arrangement of output data of YUV422 Planar (Horizontal sampling).
Luminance data of Width * Height size followed by Cb data of Width * Height/2 and Cr data of width * Height/2.
It should be as mentioned below:
[Y1 Y2 Y3 ... (Width * Height)] [Cb1 Cb2 .... (Width * Height/2)] [Cr1 Cr2 ...(Width * Height/2)]