rmagick compress and convert png to jpg - ruby

I am trying to compress a png and save it as jpg:
i = Image.read("http://ds4jk3cl4iz0o.cloudfront.net/e2558b0d34221d3270189320173dabc2.png").first
it's size is 799 kb:
http://ds4jk3cl4iz0o.cloudfront.net/e2558b0d34221d3270189320173dabc2.png=>e2558b0d34221d3270189320173dabc2.png PNG 640x639 640x639+0+0 DirectClass 8-bit 799kb
I set the format to jpeg and quality to 10 (i.e very poor quality so file size should be greatly reduced):
i.format = 'JPEG'
i.write("itest10.png") { self.quality = 10 }
The size actually increases to 800kb!
=> http://ds4jk3cl4iz0o.cloudfront.net/e2558b0d34221d3270189320173dabc2.png=>itest40.png PNG 640x639 640x639+0+0 DirectClass 8-bit 800kb
1) Why?
2) How can I compress the photo so the size is < 150kb ?
Thanks!

The use of '.png' extension will change the format back to PNG on the call to write.
There are two possible solutions.
First, I'd recommend using the normal file extension for your format if possible, because a lot of computer systems will rely on that:
i = Image.read( 'demo.png' ).first
i.format = 'JPEG'
i.write( 'demo_compressed.jpg' ) { |image| image.quality = 10 }
If this is not possible, you can set the format inside the block passed to write, and this will apply the format after the extension has been processed:
i = Image.read( 'demo.png' ).first
i.write( 'demo_compressed.png' ) do |image|
image.format = 'JPEG'
image.quality = 10
end
In both the above cases, I get the expected high compression (and low quality) jpeg format image.
This has been updated due to recent RMagick changes (thanks to titan for posting comment). The orginal code snippets were
i.write( 'demo_compressed.jpg' ) { self.quality = 10 }
and
i.write( 'demo_compressed.png' ) do
self.format = 'JPEG'
self.quality = 10
end
These may still work in older RMagick installations.

I tried the other answer and I was still having issues with the transparency. This code here work fine for me:
img_list = Magick::ImageList.new
img_list.read( 'image.png' )
img_list.new_image( img_list.first.columns, img_list.first.rows ) {
self.background_color = "white"
}
imageJPG = img_list.reverse.flatten_images
imageJPG.write( "out.jpg" )
The idea is first create an imageList then load the PNG image into it and after that add a new image to that list and set its bg to be white. Then just reverse the order of the list then flatten the image list into a single image. To add compression just do the self.quality thing from the above answer.

Related

find template image in directory of images

I have a directory of images and an image that I know is in this image directory there is a similar image in the directory saved in a different format and scaled differently, but I dont know where (about 100 000 images).
I want to look for the image and find out its filename inside this directory.
I am looking for a mostly already made soulution which I couldn't find. I found OpenCV but I would need to write code around that. Is there a project like that out there?
If there isn't could you help me make a simple C# console app using OpenCV, I tried their templates but never managed to get SURF or CudaSURF working.
Thanks
Edited as per #Mark Setchell's comment
If the image is identical, the fastest way is to get the file size of the image you are looking for and compare it with the file sizes of the images amongst which you are searching.
I suggest this first because, as Christoph clarifies in the comments, it doesn't require reading the file at all - it is just metadata.
If that yields more than one matching answer, calculate a hash (MD5 or other) and pick the filename that produces the same hash.
Again, as mentioned by Christoph in the comments, this doesn't require decoding the image, or holding the decompressed image in RAM, just checksumming it.
So in the end I used this site and modified the python code used there for searching a directory instead of a single image. There is not much code so the full thing is below:
import argparse
from ast import For, arg
import cv2
from os import listdir
from os.path import isfile, join
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str, required=True,
help="path to input image where we'll apply template matching")
ap.add_argument("-t", "--template", type=str, required=True,
help="path to template image")
args = vars(ap.parse_args())
# load the input image and template image from disk
print("[INFO] loading template...")
template = cv2.imread(args["template"])
cv2.namedWindow("Output")
cv2.startWindowThread()
# Display an image
cv2.imshow("Output", template)
cv2.waitKey(0)
# convert both the image and template to grayscale
templateGray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
imageFileNames = [f for f in listdir(args["image"]) if isfile(join(args["image"], f))]
for imageFileName in imageFileNames:
try:
imagePath = args["image"] + imageFileName
print("[INFO] Loading " + imagePath + " from disk...")
image = cv2.imread(imagePath)
print("[INFO] Converting " + imageFileName + " to grayscale...")
imageGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print("[INFO] Performing template matching for " + imageFileName + "...")
result = cv2.matchTemplate(imageGray, templateGray,
cv2.TM_CCOEFF_NORMED)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(result)
(startX, startY) = maxLoc
endX = startX + template.shape[1]
endY = startY + template.shape[0]
if maxVal > 0.75:
print("maxVal = " + str(maxVal))
# draw the bounding box on the image
cv2.rectangle(image, (startX, startY), (endX, endY), (255, 0, 0), 3)
# show the output image
cv2.imshow("Output", image)
cv2.waitKey(0)
cv2.imshow("Output", template)
except KeyboardInterrupt:
break
except:
print(imageFileName)
print("Error")
cv2.destroyAllWindows()
The code above shows any image with match value (what I guess is how much similarity there is between source and template) greater than 0.75
Probably still too low but if you want to use it tweak it to your liking.
Note that this WILL NOT work if the image is rotated and if, like me, you have a bright light source in the template other lightsources will come up as false positives
As for time it took me about 7 hours, where the script paused about every 20 minutes for a false positive until I found my image. I got through about 2/3 of all images.
as a sidenote it took 10 minutes to just build the array of files inside the directory, and it took about 500mb of ram once done
This is not the best answer so if anyone more qualified finds this feel free to write another answer.

Need to read in 16 bit unit gray scale not png or jpg data

In Tensorflow decode_png and decode_jpg are used to decode data read in from a source (file, url,…).
I have medical type images in a 8192x8192 gray scale 16 bit uint format and don’t need png or jpg decoding. These medical images are external files.
Over the last week I’ve tried numerous approaches, sifted through stack overflow, Github and Tensorflow web pages but have not been able to resolve the issue of not being able to modify the code to read in non png or jpg data. I’m modifying the pix2pix.py code:
input_paths = glob.glob(os.path.join(a.input_dir, "*.jpg"))
decode = tf.image.decode_jpeg
if len(input_paths) == 0:
input_paths = glob.glob(os.path.join(a.input_dir, "*.png"))
decode = tf.image.decode_png
path_queue = tf.train.string_input_producer(input_paths, shuffle=a.mode == "train")
reader = tf.WholeFileReader()
paths, contents = reader.read(path_queue)
raw_input = decode(contents) #
raw_input = tf.image.convert_image_dtype(raw_input, dtype=tf.float32)
I’ve tried numerous approaches to get through/by/around: “raw_input = decode(contents)“
Thanks in advance for any help/pointers.

Pillow: converting a TIFF from greyscale 16 bit to 8 bit results in fully white image

I know that there are multiple similar questions on SO, but I have tried multiple proposed solutions to no avail.
I have the following TIFF image that opens in Pillow as type='I;16'.
Google Drive link
Based on this SO question, I wrote this code to convert it:
def tiff_force_8bit(image, **kwargs):
if image.format == 'TIFF' and image.mode == 'I;16':
array = np.array(image)
normalized = (array.astype(np.uint16) - array.min()) * 255.0 / (array.max() - array.min())
image = Image.fromarray(normalized.astype(np.uint8))
return image
However, the result is a completely white image.
I have tried other solutions too, such as this:
table = [i/256 for i in range(65536)]
image = image.point(table, 'L')
with the same result: full white out.
Can anyone shed some light?
Thanks!
There's nothing wrong with your code. If you run:
# Open image
im = Image.open('NGC 281 11-01-2021 Ha 1.15.tif')
# Force to 8-bit
res = tiff_force_8bit(im)
# Check min and max of result
res.getextrema() # prints (0,255) as expected
# Save as PNG
res.save('result.png')
# Display it
res.show()
I can only guess there is a problem with your installation or the way you display the result.

Rmagick compress when filesize is bigger than X without write (upload to S3)

I would like to compress an image when its size is bigger than an amount (i.e. 1000000 - 1MB). I see compress is used with write method in Image object. I have the image in memory and I don´t want to write it in my server but in an Amazon S3.
This code is working. It´s uploading the image to my s3 path, however I would like to include the filesize check and the compress:
photo = Magick::Image.read(mmkResourceImage.href).first
s3 = AWS::S3.new
bucket = s3.buckets[bucketName]
obj = bucket.objects[key]
obj.write(photo)
Please, let me know if there is another approach to accomplish this.
To get the current size you can use Magick::Image#to_blob which will return a binary string. You can get the string's size with String#bytesize. You can scale it down with Magick::Image#scale!.
Here is an example that would resize it by 75% until it was under a 1,000,000 bytes.
while photo.to_blob.bytesize > 1_000_000
photo.scale!(0.75)
end
Here is an example that would reduce the quality instead
(0..100).step(5).reverse_each do |quality|
blob = photo.to_blob { self.quality = quality }
if blob.bytesize <= 1_000_000
photo = Magick::Image.from_blob(blob).first
break
end
end

Low quality when converting PDF to JPG

I'm attempting to use Imagemagic(RMAgick) to convert PDF-document into image. Original PDF is created from an image too(not native vector PDF).
image = Magick::Image::from_blob(original_pdf) { self.format = 'PDF' }
image[0].format = 'JPG'
image[0].to_blob
image[0].write(to_file.jpg) {
self.quality = 100
self.density = 144
}
But resulting image has too low quality, when printing. Original PDF has good quality in same time.
I'm trying to use these options
self.quality = 100
self.density = 144
or using PNG rather JPG, but all this doesn't work, only increase image size in kb ).
Assuming original_pdf is content of pdf file, e.g.:
original_pdf = File.open('from_file.pdf', 'rb').read
Then apply quality options in block of method from_blob
instead of block of method write:
image = Magick::Image::from_blob(original_pdf) do
self.format = 'PDF'
self.quality = 100
self.density = 144
end
image[0].format = 'JPG'
image[0].to_blob
image[0].write('to_file.jpg')
Look also quality options for Magick::ImageList.new method.

Resources