getImageData and .data() [duplicate] - javascript

I'm a newbie in HTML5+JS, I want to develop an hybrid app using ocrad.js.
The code given below, downloaded from github page is perfectly working for me(Chrome 32.0.1).
<html>
<head>
</head>
<body>
<script src="../ocrad.js"></script>
<script>
function OCRImage(image){
var canvas = document.createElement('canvas')
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
canvas.getContext('2d').drawImage(image, 0, 0)
return OCRAD(canvas)
}
function OCRPath(url, callback){
var image = new Image()
image.src = url;
image.onload = function(){ callback(OCRImage(image)) }
}
function OCRFile(file, callback){
var reader = new FileReader();
reader.onload = function(){ OCRPath(reader.result, callback); }
reader.readAsDataURL(file)
}
</script>
<input type="file" onchange="OCRFile(this.files[0], function(text){alert(text)})">
</body>
</html>
When I called OCRAD() API in my code its giving Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': the canvas has been tainted by cross-origin data.
My CODE
<html>
<head>
<script src="../ocrad.js"></script>
<body>
<canvas id="cancan" width="800", height="500">Test image</canvas>
<script type="text/javascript">
function imageLoaded(ev) {
element = document.getElementById("cancan");
c = element.getContext("2d");
im = ev.target;
width = element.width;
height = element.height;
c.drawImage(im, 0, 0);
var data1=OCRAD(c);
console.log(data1);
}
im = new Image();
im.src = "message.png";
im.onload = imageLoaded;
</script>
</body>
</html>
I have seen similar Stackoverflow Q&A here but it didn't help me to solve the issue. Please answer if any one had any comment on this issue who have worked with Ocrad.js.
OR
Is there anyother way to pass my image file (here message.png in second code example) as an argument to OCRFile() function in first code example ? (Simply I want to pass an image stored in an local file URL to OCRAD() Call to return text. )
Thanks in advance.... :)

It is a cross-origin issue which is a security mechanism in browsers.
You will either need to:
Move image to same origin as the page (origin = domain, port and protocol)
Request CORS usage from the other origin if you can't move the image
Use a proxy page to load the image (see one in action here - note: I do not know this site so use only for testing with non-critical data).
A request can be made like this (assuming im contains the image you want to OCR treat):
function imageLoaded(ev) {
element = document.getElementById("cancan");
c = element.getContext("2d");
width = element.width;
height = element.height;
c.drawImage(this, 0, 0); // 'this' = current image loaded
var data1 = OCRAD(c);
console.log(data1);
}
var im = new Image();
im.onload = imageLoaded; // set onload before src
im.crossOrigin = 'anonymous'; // request CORS usage before setting src
im.src = "message.png";
If a request will work is entirely up to the server which may deny the request (which is default behavior in most cases).
In that case only moving the image or setting up a proxy page to load the external image will allow usage of it. Note that file:// or local files are considered different origins.
A proxy page is in essence a page you pass the image url to as an argument. The page will then, on server side, load the image and pass the data back to your first (requesting) page. This way you can "stream" the image through your own server removing CORS restrictions but at the expense of increased traffic on your own server. Some server may also block this approach by denying external access (ie. by referrer or IP etc.)
See Cross-Origin Resource Sharing for more details.

Related

Is it possible to have JavaScript loading saved image instead of from server?

I have a page that load a random image as background image. Something like this:
<!-- some other codes -->
<style>
.random-background {
height: 100px;
width: 100px;
background-image: url("https://server-domain.com/random.php");
}
</style>
<div class="random-background"></div>
<!-- some other codes -->
https://server-domain.com/random.php will return a random image every time it is called, so using AJAX to call the link is not an option. I am writing a Chrome extension, so I have no control on the server behaviour. Is there a way, using JavaScript, I can get the exact image used as background as the user see on screen?
It may seems to be a duplicated with this question. The difference is in this question, the target element is a div instead of img, so in the suggested solution to that question,
context.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
won't work. This will be the error message
Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'.
at :6:9
I'm posting this as a potentially viable solution, but ultimately this depends on the CORS policy of the server generating the random images.
This somewhat uses the concept from Javascript: how to get image as bytes from a page (without redownloading), however to answer the differences outlined by OP, this works a little different.
The idea is that the image is first downloaded using JavaScript and applied to an Image() object created by JavaScript. When the image is done loading, it is then loaded into a canvas object on which we can apply toDataURL(). Finally this new image data can be applied to the background-image property of the <div> element and also used anywhere else needed (it would just need to be saved as global variable).
The server CORS policy has not been disclosed by OP nor has what OP needs to do with the image data, so that is not being addressed in this answer.
const _Base64Image = url => {
const img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL("image/png");
_SetBG(document.querySelector("#imgTest"), dataURL, img.width, img.height);
}
img.src = url;
}
const _SetBG = (el, data, w, h) => {
el.style.width = `${w}px`;
el.style.height = `${h}px`;
el.style.backgroundImage = `url("${data}")`;
}
_Base64Image('https://nathanchapman.gallerycdn.vsassets.io/extensions/nathanchapman/javascriptsnippets/0.2.0/1510271465098/Microsoft.VisualStudio.Services.Icons.Default');
.random-background {
height: 100px;
width: 100px;
}
<div id="imgTest" class="random-background"></div>
Why you want to do this is still a mystery to me, and the following is surely a bad idea in most cases. It will cause performance issues. But if you really need it, here goes...
You can achieve this by intercepting each image request so that you can always store the response in JS before the browser can start using it. This could get very costly, especially if you need to perform this on every request.
All you have to do is make a web worker listen to the fetch event.
Something like this:
const images = {};
function doAnythingWith(request, response) {
// response.url would not work as it changes on redirects.
// request.url should have the URL from the CSS.
images[request.url] = response.body;
}
self.addEventListener("fetch", (event) => {
if (event.request.method !== "GET") return;
// Optionally include some exlusion logic here.
// Maybe by domain or file extension.
// Prevent the default, and handle the request ourselves.
event.respondWith(
(async () => {
const response = await fetch(event.request);
doAnythingWith(request, response);
return response;
})()
);
});
self.addEventListener("message", (event) => {
if (event.type !== "get-loaded-image") return;
return images[event.payload.url];
});
Then you should be able to get the image for any given URL, and it will be the one that the user got.
myWorker.postMessage({type: "get-loaded-image", payload: someUrl});
You can store this with the URL as a key. You can then send messages to this worker to retrieve the original content for a URL.
Or perhaps you don't need to send a message at all and execute all your logic directly in the worker.

Get pixels' colors from HTML Canvas WITHOUT getImageData()?

Task
I am currently trying to create a web extension for Firefox.
It should be able to read images' pixels.
For that purpose, I am rendering the image on an invsible canvas, and then I want to read it.
Example Code
function getImdata(reference) {
var canvas=document.createElement("canvas");
canvas.width=reference.naturalWidth;
canvas.height=reference.naturalHeight;
var context=canvas.getContext("2d");
context.drawImage(reference,0,0);
return context.getImageData(0,0,reference.naturalWidth,reference.naturalHeight); //Here I actually get the error...
}
Problem
However, I am getting a "Security Error" if I use "getImageData()".
Question
So I need a workaround, but couldn't find anything myself.
How can I read images' pixels without getImageData() ?
EDIT
Apparently it has something to do with CORS : HTML5 Canvas getImageData and Same Origin Policy
Thanks in advance!
There is. Since you're running from an extension your extension will have privileged access to cross-origin sources but only if loaded via fetch() and XMLHttpRequest() from a content script (or background script) - excerpt from that link:
Content scripts get the same cross-domain privileges as the rest of
the extension: so if the extension has requested cross-domain access
for a domain using the permissions key in manifest.json, then its
content scripts get access that domain as well.
This is accomplished by exposing more privileged XHR and fetch
instances in the content script [...]
Please note that these calls when called from a content script will not set origin and referer headers which sometimes can cause problems if the cross-origin site expects these to be set - for those cases you will need to use the non-privileged content.XMLHttpRequest or content.fetch() which will bring you back to square one.
The permissions in the manifest file (or if set permissions dynamically) must also allow access to these cross-origin sites.
This means however that you will have to "reload" the image source separately via these calls. You can do this the following way by first obtaining the original URL to the image you want to load, say, from a content script:
// example loading all images in current tab
let images = document.querySelectorAll("img");
for(let image of images) loadAsBitmap(image.src); // some sub-call using the url
Then load that source via the content script's fetch():
fetch(src).then(resp => { // load from original source
return resp.blob(); // obtain a blob
}).then(blob => { // convert blob, see below
// ...
};
When the blob is obtained you can convert it to an Object-URL and set that as source for an image and be able to go around the cross-origin restriction we otherwise face. In the content script, next steps would be:
let url = URL.createObjectURL(blob); // attach Object-URL to blob
let img = new Image(); // create image element *
img.onload = () => { // attach handler
let c = document.createElement("canvas"); // create canvas
let ctx = c.getContext("2d"); // get context
c.width = img.width; // canvas size = image
c.height = img.height;
ctx.drawImage(img, 0, 0); // draw in image
URL.revokeObjectURL(url); // remove reference.
let imageData =
ctx.getImageData(0,0,c.width,c.height); // get image data
// .. callback to a function that handles the image data
};
img.src = url; // start loading blob

getImageData cross-origin error

Precursor: I know there have been a few questions already asked about this topic, but none of them seem to offer a straight JavaScript only solution.
So I ran into this error where I am trying to get the pixel data from a canvas using something like context.getImageData(), my exact code can been seen here or below:
<canvas id="imageCanvas" width="600" height="800"></canvas>
// Load Image
var canvas = document.getElementById('imageCanvas');
var image = new Image();
image.src = 'http://emoticons.pw/emoticons/emoticon-faces-002-medium.png';
image.onload = function() {
width=image.width;
height=image.height;
var context = canvas.getContext('2d');
context.fillStyle="#024359"; // canvas background color
context.fillRect(0, 0, width, height);
context.drawImage(this,0,0);
imageData = context.getImageData(0,0,width, height); // PROBLEM HERE
context.putImageData(imageData, 0, 0);
}
I get the following errors in Chrome:
Unable to get image data from canvas because the canvas has been
tainted by cross-origin data.
and then a security error as well. I don't want to make server changes or start chrome with a weird instruction thing. I feel like there has to be something I can do in JavaScript.
Using local images only is not a problem, but when trying that, I got the same error!
I am trying to do this without a server, if I put this on my "default" godaddy web server, are all my problems solved? I heard rumors dropbox could also simulate a server close enough?
You can't use file:// if you're using that (Chrome allow you to override this but I won't recommend it).
For local testing use a light-weight server such as Mongoose which allow you use http://localhost as a domain to test your local pages. This way you avoid problems with CORS.
If you need to host images on a different domain you need to make sure they support cross-origin usage.
DropBox and ImgUrl (I recommend the latter for just images) both support CORS usage, but you need to request such usage by adding the crossOrigin property to your image before setting the source (or include it in the HTML tag if you're using that):
var img = new Image;
img.onload = myLoader;
img.crossOrigin = ''; ///=anonymous
img.src = 'http://imgur.com/....';
or in HTML:
<img src="..." alt="" crossOrigin="anonymous" />
Make sure you put the img.setAttribute('crossOrigin', ''); before you set the source of your image object. just like this:
var img = document.createElement("img");
//fix crossorigin here...
img.setAttribute('crossOrigin', '');
//after that put your source
img.src = imageSrcURL;
document.body.appendChild(img);

Image src (via javascript) inconsistency between chrome/firefox

Heya, so I'm running into a weird bug/edgecase. Check out the following code:
var i = new Image();
i.src = 'http://ia.media-imdb.com/images/M/MV5BMTIxOTAxNTc4NF5BMl5BanBnXkFtZTcwOTg1NzQyMQ##._V1._SX97_SY140_.jpg';
console.log(i.width);
This works fine in chrome (as it's referring to a real image), but fails in FF. Thought it may have to do with the 'at' signs or the double extension (eg. '._V1._SX97_SY140_.jpg'), but don't really know.
Thanks for any help.
keep in mind, that the image is loaded asynchronously. You need to assign a event handler for the load event of the image and get the width there:
var i = new Image();
i.onload = function() {
console.log(this.width);
}
i.src = 'http://ia.media-imdb.com/images/M/MV5BMTIxOTAxNTc4NF5BMl5BanBnXkFtZTcwOTg1NzQyMQ##._V1._SX97_SY140_.jpg';
A 403 response header means Forbidden (wiki), that you are not allowed to access the resource.
imdb.com might be doing this to prevent hotlinking of their images in other sites.

JavaScript: how to force Image() not to use the browser cache?

If I load the nextimg URL manually in the browser, it gives a new picture every time I reload. But this bit of code shows the same image every iteration of draw().
How can I force myimg not to be cached?
<html>
<head>
<script type="text/javascript">
function draw(){
var canvas = document.getElementById('canv');
var ctx = canvas.getContext('2d');
var rx;
var ry;
var i;
myimg = new Image();
myimg.src = 'http://ohm:8080/cgi-bin/nextimg'
rx=Math.floor(Math.random()*100)*10
ry=Math.floor(Math.random()*100)*10
ctx.drawImage(myimg,rx,ry);
window.setTimeout('draw()',0);
}
</script>
</head>
<body onload="draw();">
<canvas id="canv" width="1024" height="1024"></canvas>
</body>
</html>
The easiest way is to sling an ever-changing querystring onto the end:
var url = 'http://.../?' + escape(new Date())
Some people prefer using Math.random() for that instead of escape(new Date()). But the correct way is probably to alter the headers the web server sends to disallow caching.
You can't stop it from caching the image altogether within Javascript. But, you can toy with the src/address of the image to force it to cache anew:
[Image].src = 'image.png?' + (new Date()).getTime();
You can probably take any of the Ajax cache solutions and apply it here.
That actually sounds like a bug in the browser -- you could file at http://bugs.webkit.org if it's in Safari or https://bugzilla.mozilla.org/ for Firefox. Why do i say potential browser bug? Because the browser realises it should not be caching on reload, yet it does give you a cached copy of the image when you request it programmatically.
That said are you sure you're actually drawing anything? the Canvas.drawImage API will not wait for an image to load, and is spec'd to not draw if the image has not completely loaded when you try to use it.
A better practice is something like:
var myimg = new Image();
myimg.onload = function() {
var rx=Math.floor(Math.random()*100)*10
var ry=Math.floor(Math.random()*100)*10
ctx.drawImage(myimg,rx,ry);
window.setTimeout(draw,0);
}
myimg.src = 'http://ohm:8080/cgi-bin/nextimg'
(You can also just pass draw as an argument to setTimeout rather than using a string, which will save reparsing and compiling the same string over and over again.)
There are actually two caches you need to bypass here: One is the regular HTTP cache, that you can avoid by using the correct HTTP headers on the image. But you've also got to stop the browser from re-using an in-memory copy of the image; if it decides it can do that it will never even get to the point of querying its cache, so HTTP headers won't help.
To prevent this, you can use either a changing querystring or a changing fragment identifier.
See my post here for more details.

Categories

Resources