Converting GIF to JPEG with Shrine and MiniMagick - ruby

I have this project in ruby on rails and recently I changed the upload image functionality to Shrine.
I want be able to upload an animated gif and then create an static jpeg (or gif if it's easier) derivative.
I am trying this way
class GifUploader < Shrine
Attacher.derivatives do |original|
magick = ImageProcessing::MiniMagick
{
static: magick.loader(page: 0)
.loader(geometry: "450x250")
.call(original),
}
end
end
With this approach I received this error
MiniMagick::Error: `convert /tmp/shrine20211117-94958-ieep9h.gif[0][450x250] -au
to-orient /tmp/image_processing20211117-94958-q3i2qe.gif` failed with error:
convert-im6.q16: unable to open image `/tmp/shrine20211117-94958-ieep9h.gif[0][450x250]': No such file or directory # error/blob.c/OpenBlob/2924.
convert-im6.q16: no decode delegate for this image format `' # error/constitute.c/ReadImage/575.
convert-im6.q16: no images defined `/tmp/image_processing20211117-94958-q3i2qe.gif' # error/convert.c/ConvertImageCommand/3229.
I also tried a different approach and gave me the same error:
require "image_processing/mini_magick"
class GifUploader < Shrine
Attacher.derivatives do |original|
magick = ImageProcessing::MiniMagick.source(original)
{
static: magick # original is a IO object
.loader(page: 0)
.loader(geometry: "450x250")
.convert!("jpeg")
.call(original),
}
end
end
This is my first project in ruby on rails and I don't know what is happening. I already searched for it, but no results
I am using ruby on rails 6

Okay, I managed to fix this. Basically I inverted the order and get rid of any "!" points. I also deleted the loader(geometry: "450x250")
The code:
require "image_processing/mini_magick"
class GifUploader < Shrine
Attacher.derivatives do |original|
magick = ImageProcessing::MiniMagick.source(original)
{
static: magick.convert("jpeg").loader(page: 0).call
}
end
end
I don't know why this worked, if anyone knows please explain in the comments

Related

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.

Reading image file (file storage object) using OpenCV

I am sending an image by curl to flask server, i am using this curl command
curl -F "file=#image.jpg" http://localhost:8000/home
and I am trying to read the file using OpenCV on the server side.
On the server side I handle the image by this code
#app.route('/home', methods=['POST'])
def home():
data =request.files['file']
img = cv.imread(data)
fact_resp= model.predict(img)
return jsonify(fact_resp)
I am getting this error-
img = cv.imread(data)
TypeError: expected string or Unicode object, FileStorage found
How do I read the file using OpenCV on the server side?
Thanks!
I had similar issues while using opencv with flask server, for that first i saved the image to disk and read that image using saved filepath again using cv.imread()
Here is a sample code:
data =request.files['file']
filename = secure_filename(file.filename) # save file
filepath = os.path.join(app.config['imgdir'], filename);
file.save(filepath)
cv.imread(filepath)
But now i have got even more efficient approach from here by using cv.imdecode() to read image from numpy array as below:
#read image file string data
filestr = request.files['file'].read()
#convert string data to numpy array
file_bytes = numpy.fromstring(filestr, numpy.uint8)
# convert numpy array to image
img = cv.imdecode(file_bytes, cv.IMREAD_UNCHANGED)
After a bit of experimentation, I myself figured out a way to read the file using CV2.
For this I first read the image using PIL.image method
This is my code,
#app.route('/home', methods=['POST'])
def home():
data =request.files['file']
img = Image.open(request.files['file'])
img = np.array(img)
img = cv2.resize(img,(224,224))
img = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)
fact_resp= model.predict(img)
return jsonify(fact_resp)
I wonder if there is any straight forward way to do this without using PIL.
So incase you want to do something like ,
file = request.files['file']
img = cv.imread(file)
then do it like this
import numpy as np
file = request.files['file']
file_bytes = np.fromfile(file, np.uint8)
file = cv.imdecode(file_bytes, cv.IMREAD_COLOR)
Now you don't need to do cv.imread() again, but can use this in the next line of codes.
This applies to OpenCV v3.x and onwards
Two-line solution, change grayscale to what you need
file_bytes = numpy.fromfile(request.files['image'], numpy.uint8)
# convert numpy array to image
img = cv.imdecode(file_bytes, cv.IMREAD_GRAYSCALE)

ruby picture download verification

I would like to know if it is possible to check if a picture downloaded using open-uri () was downloaded correctly?
There are 2 cases I would like to manage:
1. the picture is at least 80% black (in this case, it is an error)
2. the picture is simply unreadable by picture reader (ex: Ephoto on Linux)
Ideally, the code would look somewhat like this
pic_buffer = open(my_link, "User-Agent" => "Ruby/#{RUBY_VERSION}")
if functionCheckPictureDownloadedCorrectly(pic_buffer) == false
abort("file is unreadable")
end
puts "file is good, saving it"
File.open(name_buffer + ".jpg", 'wb') do |pic|
pic << pic_buffer.read
end
Note : I am only downloading .jpeg pictures

rmagick compress and convert png to jpg

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.

How to remove link tag from image using Nokogiri

I'm parsing an HTML document using Nokogiri. The code contain several images like this:
<img alt="alternative-text" border="0" height="427" src="http://url_to_my_photo.jpg?" title="Image Title" width="640">
I'm trying to save that image to my S3 storage, change the style and remove the link. All the images have the css tag ".post-body img".
So far, the closest I got is this:
#doc.css(".post-body img").each do |image|
#new_photo = Photo.create!(
#Params required to save and upload the photo to S3.
...
...
)
# The url of the photo upload to S3 is #new_photo.photo.url
image['src']= #new_photo.photo.url
image['class'] = "my-picture-class"
image.parent['src] = '#'
puts image.parent.content
#doc.to_html
end
This removes the link to the big photo but obviously it isn't a good solution.
I've tried to replace the parent using image.parent << image as suggested on http://rubyforge.org/pipermail/nokogiri-talk/2009-June/000333.html but doesn't do anything and image.parent = image returns "Could not reparent node (RuntimeError)"
To convert that mailing list example over to apply to your situation, you have to remember that node is the node they are trying to get rid of, which in your case is image.parent.
So instead of image.parent['src] = '#' you should try:
link = image.parent
link.parent << image
link.remove
Edit:
Actually, the above code would probably move all the images to the bottom of whatever element contains the link, so try this instead:
link = image.parent
link.replace(image)

Resources