Getting error while try to write gif image to buffer - vips

I am using PHP libvips library when I am using this function writeToBuffer for write to buffer the image it gives me below types of error.
Fatal error: Uncaught Jcupitt\Vips\Exception: magicksave_buffer: libMagick error: no decode delegate for this image format `' # error/blob.c/ImagesToBlob/2413
Note - This error occurs only when my image type is gif
$imagePathInfo = pathinfo($inputFileName);
$imgExtension = $imagePathInfo['extension'];
$img = Vips\Image::newFromFile($inputFileName, ['access' => 'sequential']);
$img = $img->writeToBuffer('.' . $imgExtension);

By default libvips will only load the first frame of an animation. To load all frames, set the n parameter (number of pages) to -1. Use:
$img = Vips\Image::newFromFile($inputFileName, [
'access' => 'sequential',
'n' => -1
]);
libvips 8.11 uses imagemagick to write GIF images. You need to tell imagemagick what format to write in with the format parameter, eg.:
$img = $img->writeToBuffer('.' . $imgExtension, [
'format' => $imgExtension
]);
libvips 8.12 (due by the end of Nov 2021) will automatically pass the correct format value to imagemagick, and also has a dedicated and much faster GIF writer.

Related

Google Cloud Video Intelligence API in Python - Unable to run object tracking on multiple videos in a folder

I'm trying to run object tracking on a folder containing multiple videos. There are 5 videos in my bucket and following the documentation from here, it suggests using the wildcard (*) operator. However, when I run the entire script, only 1 video gets annotated and not the entire folder containing 5 videos. Also, response2.json does not get created as the output_uri in my GCS bucket.
To identify multiple videos, a video URI may include wildcards in the object-id. Supported wildcards: ' * ' to match 0 or more characters; ‘?’ to match 1 character.
https://googleapis.dev/python/videointelligence/latest/gapic/v1/types.html
Which is what I've done in my input_uri bit of the code:
gcs_uri = 'gs://video_intel/*'
If you check the screenshot, it should the bucket id name and shows multiple videos in the same folder.
Can anyone pls help with this question. Thanks.
Full script:
import os
os.environ['GOOGLE_APPLICATION_CREDENTIALS']='poc-video-intelligence-da5d4d52cb97.json'
"""Object tracking in a video stored on GCS."""
from google.cloud import videointelligence
video_client = videointelligence.VideoIntelligenceServiceClient()
features = [videointelligence.enums.Feature.OBJECT_TRACKING]
gcs_uri = 'gs://video_intel/*'
output_uri = 'gs://video_intel/response2.json'
operation = video_client.annotate_video(input_uri=gcs_uri, features=features, output_uri=output_uri)
print("\nProcessing video for object annotations.")
result = operation.result(timeout=300)
print("\nFinished processing.\n")
# The first result is retrieved because a single video was processed.
object_annotations = result.annotation_results[0].object_annotations
for object_annotation in object_annotations:
print("Entity description: {}".format(object_annotation.entity.description))
if object_annotation.entity.entity_id:
print("Entity id: {}".format(object_annotation.entity.entity_id))
print(
"Segment: {}s to {}s".format(
object_annotation.segment.start_time_offset.seconds
+ object_annotation.segment.start_time_offset.nanos / 1e9,
object_annotation.segment.end_time_offset.seconds
+ object_annotation.segment.end_time_offset.nanos / 1e9,
)
)
print("Confidence: {}".format(object_annotation.confidence))
# Here we print only the bounding box of the first frame in the segment
frame = object_annotation.frames[0]
box = frame.normalized_bounding_box
print(
"Time offset of the first frame: {}s".format(
frame.time_offset.seconds + frame.time_offset.nanos / 1e9
)
)
print("Bounding box position:")
print("\tleft : {}".format(box.left))
print("\ttop : {}".format(box.top))
print("\tright : {}".format(box.right))
print("\tbottom: {}".format(box.bottom))
print("\n")
please modify 'gcs_uri = 'gs://video_intel/'' to gcs_uri = 'gs://video_intel/.*'

How to pass base64 encoded image to Tensorflow prediction?

I have a google-cloud-ml model that I can run prediction by passing a 3 dimensional array of float32...
{ 'instances' [ { 'input' : '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]' } ] }
However this is not an efficient format to transmit images, so I'd like to pass base64 encoded png or jpeg. This document talks about doing that, but what is not clear is what the entire json object looks like. Does the { 'b64' : 'x0welkja...' } go in place of the '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]', leaving the enclosing 'instances' and 'input' the same? Or some other structure? Or does the tensorflow model have to be trained on base64?
The TensorFlow model does not have to be trained on base64 data. Leave your training graph as is. However, when exporting the model, you'll need to export a model that can accept PNG or jpeg (or possibly raw, if it's small) data. Then, when you export the model, you'll need to be sure to use a name for the output that ends in _bytes. This signals to CloudML Engine that you will be sending base64 encoded data. Putting it all together would like something like this:
from tensorflow.contrib.saved_model.python.saved_model import utils
# Shape of [None] means we can have a batch of images.
image = tf.placeholder(shape = [None], dtype = tf.string)
# Decode the image.
decoded = tf.image.decode_jpeg(image, channels=3)
# Do the rest of the processing.
scores = build_model(decoded)
# The input name needs to have "_bytes" suffix.
inputs = { 'image_bytes': image }
outputs = { 'scores': scores }
utils.simple_save(session, export_dir, inputs, outputs)
The request you send will look something like this:
{
"instances": [{
"b64": "x0welkja..."
}]
}
If you just want an efficient way to send images to a model (and not necessarily base-64 encode it), I would suggest uploading your images(s) to Google Cloud Storage and then having your model read off GCS. This way, you are not limited by image size and you can take advantage of multi-part, multithreaded, resumable uploads etc. that the GCS API provides.
TensorFlow's tf.read_file will directly off GCS. Here's an example of a serving input_fn that will do this. Your request to CMLE would send it an image URL (gs://bucket/some/path/to/image.jpg)
def read_and_preprocess(filename, augment=False):
# decode the image file starting from the filename
# end up with pixel values that are in the -1, 1 range
image_contents = tf.read_file(filename)
image = tf.image.decode_jpeg(image_contents, channels=NUM_CHANNELS)
image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
image = tf.expand_dims(image, 0) # resize_bilinear needs batches
image = tf.image.resize_bilinear(image, [HEIGHT, WIDTH], align_corners=False)
#image = tf.image.per_image_whitening(image) # useful if mean not important
image = tf.subtract(image, 0.5)
image = tf.multiply(image, 2.0) # -1 to 1
return image
def serving_input_fn():
inputs = {'imageurl': tf.placeholder(tf.string, shape=())}
filename = tf.squeeze(inputs['imageurl']) # make it a scalar
image = read_and_preprocess(filename)
# make the outer dimension unknown (and not 1)
image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])
features = {'image' : image}
return tf.estimator.export.ServingInputReceiver(features, inputs)
Your training code will train off actual images, just as in rhaertel80's suggestion above. See https://github.com/GoogleCloudPlatform/training-data-analyst/blob/master/courses/machine_learning/deepdive/08_image/flowersmodel/trainer/task.py#L27 for what the training/evaluation input functions would look like.
I was trying to use #Lak's answer (thanks Lak) to get online predictions for multiple instances in one json file, but kept getting the following error (I had two instances in my test json, hence the shape [2]):
input filename tensor must be scalar but had shape [2]
The problem is that ML engine apparently batches all the instances together and passes them to the serving inpur receiver function, but #Lak's sample code assumes the input is a single instance (it indeed works fine if you have a single instance in your json). I altered the code so that it can process a batch of inputs. I hope it will help someone:
def read_and_preprocess(filename):
image_contents = tf.read_file(filename)
image = tf.image.decode_image(image_contents, channels=NUM_CHANNELS)
image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
return image
def serving_input_fn():
inputs = {'imageurl': tf.placeholder(tf.string, shape=(None))}
filename = inputs['imageurl']
image = tf.map_fn(read_and_preprocess, filename, dtype=tf.float32)
# make the outer dimension unknown (and not 1)
image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])
features = {'image': image}
return tf.estimator.export.ServingInputReceiver(features, inputs)
The key changes are that 1) you don't squeeze the input tensor (that would cause trouble in the special case when your json contains only one instance) and, 2) use tf.map_fn to apply the read_and_preprocess function to a batch of input image urls.

Ionic 2 / cordova-plugin-file File.writeFile() refuses to create binary file correctly (png image)

In summary
File.writeFile() creates a PNG file of 0 bytes when trying to write a Blob made from base64 data.
In my application, I am trying to create a file that consists of base64 data stored in the db. The rendered equivalent of the data is a small anti-aliased graph curve in black on a transparent background (never more that 300 x 320 pixels) that has previously been created and stored from a canvas element. I have independently verified that the stored base64 data is indeed correct by rendering it at one of various base64 encoders/decoders available online.
Output from "Ionic Info"
--------------------------------
Your system information:
Cordova CLI: 6.3.1
Gulp version: CLI version 3.9.1
Gulp local:
Ionic Framework Version: 2.0.0-rc.2
Ionic CLI Version: 2.1.1
Ionic App Lib Version: 2.1.1
Ionic App Scripts Version: 0.0.39
OS:
Node Version: v6.7.0
--------------------------------
The development platform is Windows 10, and I've been testing directly on a Samsung Galaxy S7 and S4 so far.
I know that the base64 data has to be converted into binary data (as a Blob) first, as File does not yet support writing base64 directly in to an image file. I found various techniques with which to do this, and the code which seems to suit my needs the most (and reflects a similar way I would have done it in java is illustrated below):
Main code from constructor:
this.platform.ready().then(() => {
this.graphDataService.getDataItem(this.job.id).then((data) =>{
console.log("getpic:");
let imgWithMeta = data.split(",")
// base64 data
let imgData = imgWithMeta[1].trim();
// content type
let imgType = imgWithMeta[0].trim().split(";")[0].split(":")[1];
console.log("imgData:",imgData);
console.log("imgMeta:",imgType);
console.log("aftergetpic:");
// this.fs is correctly set to cordova.file.externalDataDirectory
let folderpath = this.fs;
let filename = "dotd_test.png";
File.resolveLocalFilesystemUrl(this.fs).then( (dirEntry) => {
console.log("resolved dir with:", dirEntry);
this.savebase64AsImageFile(dirEntry.nativeURL,filename,imgData,imgType);
});
});
});
Helper to convert base64 to Blob:
// convert base64 to Blob
b64toBlob(b64Data, contentType, sliceSize) {
//console.log("data packet:",b64Data);
//console.log("content type:",contentType);
//console.log("slice size:",sliceSize);
let byteCharacters = atob(b64Data);
let byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
let slice = byteCharacters.slice(offset, offset + sliceSize);
let byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
let byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
console.log("size of bytearray before blobbing:", byteArrays.length);
console.log("blob content type:", contentType);
let blob = new Blob(byteArrays, {type: contentType});
// alternative way WITHOUT chunking the base64 data
// let blob = new Blob([atob(b64Data)], {type: contentType});
return blob;
}
save the image with File.writeFile()
// save the image with File.writeFile()
savebase64AsImageFile(folderpath,filename,content,contentType){
// Convert the base64 string in a Blob
let data:Blob = this.b64toBlob(content,contentType,512);
console.log("file location attempt is:",folderpath + filename);
File.writeFile(
folderpath,
filename,
data,
{replace: true}
).then(
_ => console.log("write complete")
).catch(
err => console.log("file create failed:",err);
);
}
I have tried dozens of different decoding techniques, but the effect is the same. However, if I hardcode simple text data into the writeFile() section, like so:
File.writeFile(
folderpath,
"test.txt",
"the quick brown fox jumps over the lazy dog",
{replace: true}
)
A text file IS created correctly in the expected location with the text string above in it.
However, I've noticed that whether the file is the 0 bytes PNG, or the working text file above, in both cases the ".then()" consequence clause of the File Promise never fires.
Additionally, I swapped the above method and used the Ionic 2 native Base64-To-Gallery library to create the images, which worked without a problem. However, having the images in the user's picture gallery or camera roll is not an option for me as I do not wish to risk a user's own pictures while marshalling / packing / transmitting / deleting the data-rendered images. The images should be created and managed as part of the app.
User marcus-robinson seems to have experienced a similar issue outlined here, but it was across all file types, and not just binary types as seems to be the case here. Also, the issue seems to have been closed:
https://github.com/driftyco/ionic/issues/5638
Anybody experiencing something similar, or possibly spot some error I might have caused? I've tried dozens of alternatives but none seem to work.
I had similar behaviour saving media files which worked perfectly on iOS. Nonetheless, I had the issue of 0 bytes file creation on some Android devices in release build (dev build works perfectly). After very long search, I followed the following solution
I moved the polyfills.js script tag to the top of the index.html in the ionic project before the cordova.js tag. This re-ordering somehow the issue is resolved.
So the order should look like:
<script src="build/polyfills.js"></script>
<script type="text/javascript" src="cordova.js"></script>
Works on ionic 3 and ionic 4.
The credits go to 1
I got that working with most of your code:
this.file.writeFile(this.file.cacheDirectory, "currentCached.jpeg", this.b64toBlob(src, "image/jpg", 512) ,{replace: true})
The only difference i had was:
let byteCharacters = atob(b64Data.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''));
instead of your
let byteCharacters = atob(b64Data);
Note: I did not use other trimming etc. like those techniques you used in your constructor class.

Can I convert browser generated image blob to image file for upload?

I'm using fabric.js to dynamically create textures in Threes.js, and I need to save the textures to AWS. I'm using meteor-slingshot, which normally takes images passed in through a file selector input. Here's the uploader:
var uploader = new Slingshot.Upload("myFileUploads");
uploader.send(document.getElementById('input').files[0], function (error, downloadUrl) {
if (error) {
console.error('Error uploading', uploader.xhr.response);
alert (error);
}
else {
Meteor.users.update(Meteor.userId(), {$push: {"profile.files":downloadUrl}});
}
});
Uploading works fine from the drive ... but I'm generating my files in the browser, not getting them from the drive. Instead, they are generated from a canvas element with the following method:
generateTex: function(){
var canvTex = document.getElementById('texture-generator');
var canvImg = canvTex.toDataURL('image/jpeg');
var imageNew = document.createElement( 'img' );
imageNew.src = canvImg;
}
This works great as well. If I console.log the imageNew, I get my lovely image with base 64 encoding:
<img src=​"data:​image/​jpeg;​base64,/​9j/​
4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgID
//....carries on to 15k or so characters
If I console.log a file object added from the drive via filepicker ( not generated from a canvas ), I can see what the file object should look like:
file{
lastModified: 1384216556000
lastModifiedDate: Mon Nov 11 2013 16:35:56 GMT-0800 (PST)
name: "filename.png"
size: 3034
type: "image/png"
webkitRelativePath: ""
__proto__: File
}
But I can't create a file from the blob for upload, because there is no place in the file object to add the actual data.
To sum up I can:
Generate an image blob and display it in a dom element
Upload files from the drive using meteor-slingshot
inspect the existing file object
But I don't know how to convert the blob into a named file, so I can pass it to the uploader.
I don't want to download the image, (there are answers for that), I want to upload it. There is a "chrome only" way to do this with the filesystem API but I need something cross browser (and eventually cross platform). If someone could help me with this, I would have uncontainable joy.
Slingshot supports blobs just as well as files: https://github.com/CulturalMe/meteor-slingshot/issues/22
So when you have a canvas object called canvTex and a Slingshot.Upload instance called uploader, then uploading the canvas image is as easy as:
canvTex.toBlob(function (blob) {
uploader.send(blob, function (error, downloadUrl) {
//...
});
});
Because blobs have no names, you must take that into account when defining your directive. Do not attempt to generate a key based on the name of the file.

How to attach carrier wave version inline in email

I have a tidbit model that has a carrierwave uploader. Im working on attaching an inline image in an email. If I do this:
#filename = #tidbit.image.instance_variable_get('#file').filename
attachments.inline[#filename] = #tidbit.image.read
I get an inline image in my email. However, it is the full size original version.
How would I inline attach a specific version (i.e.. :thumb) of the image?
If I do:
attachments.inline[#filename] = #tidbit.image(:thumb).read
I get an argument error 1 for 0.
Late reply, but it might help googlers and I had to do something similar. The following worked:
These are the versions present on my uploader class
version :thumb do
process :resize_to_fill => [122, 70]
end
version :medium do
process :resize_to_fill => [470, 470]
end
So to get the image in a certain version I just need to do, for example:
specific_version = uploader.image.medium.read
Where medium is the version I want.
In the original question's case you need to do:
attachments.inline[#filename] = #tidbit.image.thumb.read