Is there a way to use jQuery or Javascript to detect the colorspace of a JPG image that is being uploaded via file input? To be more accurate I need a way to kick back a response when a user attempts to upload a CMYK colorspaced JPG. I would like to catch this on the client side if at all possible. I would prefer to do this using jQuery/js, but I'm definitely open to libraries that may have this functionality. Thanks in advance for any help.
I've looked around a bit, and it seems like JavaScript could be powerful enough to do this, albeit possibly very slowly.
Here is a stackoverflow question that deals with using JavaScript for face detection: Any library for face recognition in JavaScript?
I know that doesn't answer the question specifically, but it does demonstrate that JavaScript could potentially be leveraged for what you want.
Here is a question that deals with using JS along with the canvas object to get the average color of an image: Get average color of image via Javascript
There are some answers there that may be helpful for you. One of the answers there links to a library called Color Thief that could do what you want: https://github.com/lokesh/color-thief
If no one has already built what you want, my takeaway here is that it would be a laborious task, which may or may not yield results fast enough to be useful.
I had a comment that you could do a jquery ajax call to get the raw data. I don't think that is actually true, as there is no way to get the raw data. However, many browsers support the "arraybuffer" responseType of an XML http request, and you can use that. Here is a code snippet:
var img_url = "/images/testimg.jpg";
var req = new XMLHttpRequest();
req.open("GET", img_url, true);
req.responseType = "arraybuffer";
req.onload = function (event)
{
var buf = req.response;
var num_channels = null;
if (buf)
{
var img = new Uint8Array(buf);
for (var i=0; i<img.length-9; i++)
{
if (img[i]==0xFF && (img[i+1]==0xC0 || img[i+1]==0xC1))
{
var num_channels = img[i+9];
break;
}
}
}
if (num_channels==4)
console.log("This is a four-channel image.");
};
req.send(null);
Obviously, you'd want to actually do something with the result, and there should be better checking to make sure this IS a JPEG and not a random collection of bytes. Furthermore, this only will work on Baseline and Extended JPEGs, not progressive, arithmetic encoding, JPEG 2000, or anything else.
This doesn't really test if the colorspace is CMYK (or YCCK), just that it has four components, but the basic idea is there.
Related
My website is taking longer to load than is optimal because it requests a lot of small files, and those small files eventually take up a lot of requests. The ones I have the most control over are a number of data files which will always be loaded, but having them as separate files makes the process of generating them easier. Is there a way that I could put one HTTP request to a (tar?) file, and then process that efficiently with JavaScript? This is the function that I am using right now to read in the data files. What I would really like is a way to request one file that can be easily parsed. The file structure is very simple, just a collection of 4 byte floats, in a repeating pattern. I suppose I could, and I might, combine them in to a slightly more complex data structure, but if there's a way to just combine all of these files and read it in JavaScript I would love to se it!
Also of some note is some small icon files, I have a dozen or so of those that I would love to do the same thing, combine in to a single file and just load that one file.
function loadBinaryFloatArray(url, convertFunction,variable_name, onLoaded) {
var mRequest = new XMLHttpRequest();
mRequest.open('GET', url);
mRequest.responseType = 'arraybuffer';
mRequest.onreadystatechange = function () {
if (this.readyState === 4) {
// Get bytes
var buffer = this.response;
var dataview = new DataView(buffer);
// Create buffer (4 bytes / float)
var mFloatArray = new Float64Array(buffer.byteLength / 8);
// Copy floats
for (var i = 0; i < mFloatArray.length; i++)
{
mFloatArray[i] = dataview.getFloat64(i * 8,true); // At every 8th byte
}
onLoaded(convertFunction(Array.prototype.slice.call(mFloatArray)),variable_name)
}
};
mRequest.send();
}
Well, the simplest things for your icons would be to use a spritemap.
That being said. This is normally not a thing you should do: Join files on the server, because then you have to resend this HUGE request if it fails.
I also took a look at your website. For me it loads pretty fast. The major problem is that you keep requesting the same image (WhereIsRoadster.png) over and over again, which is probably the thing slowing your website down. Or it might also be down to your internet connection. Without more details, there is not much more I can tell you.
I have three failing versions of the following code in a chrome extension, which attempts to intercept a click to a link pointing to a pdf file, fetch that file, convert it to base64, and then log it. But I'm afraid I don't really know anything about binary formats and encodings, so I'm royally sucking this up.
var links = document.getElementsByTagName("a");
function transform(blob) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(blob)));
};
function getlink(link) {
var x = new XMLHttpRequest();
x.open("GET", link, true);
x.responseType = 'blob';
x.onload = function(e) {
console.log("Raw response:");
console.log(x.response);
console.log("Direct transformation:");
console.log(btoa(x.response));
console.log("Mysterious thing I got from SO:");
console.log(transform(x.response));
window.location.href = link;
};
x.onerror = function (e) {
console.error(x.statusText);
};
x.send(null);
};
for (i = 0, len = links.length; i < len; i++) {
var l = links[i]
l.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
getlink(this.href);
}, false);
};
Version 1 doesn't have the call to x.responseType, or the call to transform. It was my original, naive, implementation. It threw an error: "The string to be encoded contains characters outside of the Latin1 range."
After googling that error, I found this prior SO, which suggests that in parsing an image:
The response type needs to be set to blob. So this code does that.
There's some weird line, I don't know what it does at all: String.fromCharCode.apply(null, new Uint8Array(blob)).
Because I know nothing about binary formats, I guessed, probably stupidly, that making a PDF base64 would be the same as making some random image format base64. So, in fine SO tradition, I copied code that I don't really understand. In stages.
Version 2 of the code just set the response type to blob but didn't try the second transformation. And the code worked, and logged something that looked like a base64 string, but a clearly incorrect string. In its entirety, it logged:
W29iamVjdCBCbG9iXQ==
Which is just goofily wrong. It's obviously too short for a 46k pdf file, and a reference base64 encoding I created with python from the commandline was much much much longer, as one would expect.
Version 3 of the code then also applies the mysterious transformation using stringFromCharCode and all the rest, which I shoved into the transform function.
However, that doesn't log anything at all---a blank line appears in the console in its appropriate place. No errors, no nonsense output, just a blank line.
I know I'm getting the correct file from prior testing. Also, the call to log the raw response object produces Blob {size: 45587, type: "application/pdf"}, which is the correct filesize for the pdf I'm experimenting with, so the blob actually contains what it should when it gets into the browser.
I'm using, and only need to support, a current version of chrome.
Can someone tell me what I'm doing wrong?
Thanks!
If you only need to support modern browsers, you should also be able to use FileReader#readAsDataURL.
That would let you do something like this:
var reader = new FileReader();
reader.addEventListener("load", function () {
console.log(reader.result);
}, false);
// The function accepts Blobs and Files
reader.readAsDataURL(x.response);
This logs a data URI, which will contain your base64 data.
I think I've found my own solution. The response type needs to be arraybuffer not blob.
I'm working on a testing framework that needs to pass files to the drop listener of a PLUpload instance. I need to create blob objects to pass inside a Data Transfer Object of the sort generated on a Drag / Drop event. I have it working fine for text files and image files. I would like to add support for PDF's, but it seems that I can't get the encoding right after retrieving the response. The response is coming back as text because I'm using Sahi to retrieve it in order to avoid Cross-Domain issues.
In short: the string I'm receiving is UTF-8 encoded and therefore the content looks like you opened a PDF with a text editor. I am wondering how to convert this back into the necessary format to create a blob, so that after the document gets uploaded everything looks okay.
What steps do I need to go through to convert the UTF-8 string into the proper blob object? (Yes, I am aware I could submit an XHR request and change the responseType property and (maybe) get closer, however due to complications with the way Sahi operates I'm not going to explain here why I would prefer not to go this route).
Also, I'm not familiar enough but I have a hunch maybe I lose data by retrieving it as a string? If that's the case I'll find another approach.
The existing code and the most recent approach I have tried is here:
var data = '%PDF-1.7%����115 0 obj<</Linearized 1/L ...'
var arr = [];
var utf8 = unescape(encodeURIComponent(data));
for (var i = 0; i < utf8.length; i++) {
arr.push(utf8.charCodeAt(i));
}
var file = new Blob(arr, {type: 'application/pdf'});
It looks like you were close. I just did this for a site which needed to read a PDF from another website and drop it into a fileuploader plugin. Here is what worked for me:
var url = "http://some-websites.com/Pdf/";
//You may not need this part if you have the PDF data locally already
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
//console.log(this.response, typeof this.response);
//now convert your Blob from the response into a File and give it a name
var fileOfBlob = new File([this.response], 'your_file.pdf');
// Now do something with the File
// for filuploader (blueimp), just use the add method
$('#fileupload').fileupload('add', {
files: [ fileOfBlob ],
fileInput: $(this)
});
}
}
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
I found help on the XHR as blob here. Then this SO answer helped me with naming the File. You might be able to use the Blob by itself, but you won't be able to give it a name unless its passed into a File.
Say I have an <img/>. This img has src='http://somelocation/getmypic'. At some later time, I may want to change the content of the img based on an ajax call that receives binary data. I only know once I have received the data in the ajax callback whether I want to apply it to the image. I might decide not to.
As far as I can tell from the research that I have done, the only way to set binary data on the img is by base64 encoding the binary data and setting the img src to the resultant base 64 encoded string.
My first and main question is: what is the overhead? Doesn't this mean that I have tripled the processing and memory usage for an image download? My ajax callback gets the binary data that has been loaded into memory, then I base 64 encode the binary stream and then (I assume) the browser decodes this stream back to binary? Or am I missing something and it's actually not that bad?
My secondary question is: Why is there no way to set binary data directly into the img without base64encoding? Most UI languages that I have used allow you to load an image into a 'PictureBox' (for example ) from file and also draw in the 'PictureBox'. Why is this not allowed with an img? I am thinking img.binary=bytes which would set src to 'binary' or something like that. It seems this functionality is split between img and canvas, but sometimes you want to do both. I am just wondering if I am missing something in my understanding (e.g. there is some design or implementation benefit in not allowing binary data to be set directly on img).
Thank you.
Well, this is what I came up with. I am not sure what the performance or memory usage is, but it is a lot easier than base64 encoding and it works with the asp.net MVC FileStreamResult. So, I thought that I would post it here in case it helps somebody else:
var oReq = new XMLHttpRequest();
oReq.open("post", '/somelocation/getmypic', true );
oReq.responseType = "blob";
oReq.onload = function ( oEvent )
{
var blob = oReq.response;
var imgSrc = URL.createObjectURL( blob );
var $img = $( '<img/>', {
"alt": "test image",
"src": imgSrc
} ).appendTo( $( '#bb_theImageContainer' ) );
window.URL.revokeObjectURL( imgSrc );
};
oReq.send( null );
The basic idea is that the data is not tampered with and it is received as a blob. A url is constructed for that blob (I think that essentially means that you can use a URL to access that blob in memory). But the good thing is that you can set the img src to this, which is...awesome.
This answer put me on the right track, then this page and this page on mdn. Note supported browsers.
I have ZigJS running in the browser and everything is working well, but I want to record the Kinect webcam images in order to play them back as a recorded video. I've looked through the documentation at http://zigfu.com/apidoc/ but cannot find anything related to the RGB information.
However, this SO answer leads me to believe this is possible:
We also support serialization of the depth and RGB image into canvas objects in the browser
Is it possible to capture the RGB image data from ZigJS and if so how?
Assuming you have plugin version 0.9.7, something along the lines of:
var plugin = document.getElementById("ZigPlugin"); // the <object> element
plugin.requestStreams(false, true, false); // tell the plugin to update the RGB image
plugin.addEventListener("NewFrame", function() { // triggered every new kinect frame
var rgbImage = Base64.decode(plugin.imageMap);
// plugin.imageMapResolution stores the resolution, right now hard-coded
// to QQVGA (160x120 for CPU-usage reasons)
// do stuff with the image
}
Also, I recommend you take the base64 decoder I wrote, from, say, http://motionos.com/webgl because it's an order of magnitude faster than the random javascript decoders I found via Google.
If you have version 0.9.8 of the plugin, there was an API change, so you should call:
plugin.requestStreams({updateImage:true});