-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blocking UI thread when use analyze function #47
Comments
Looks like we need to include the build files here: https://github.com/briangonzalez/rgbaster.js/blob/master/package.json#L27 Also, would you mind including the image you're processing? Cheers. |
Hi @briangonzalez, Not sure I follow:
Whatever we add to
This looks to me like what I'd expect. I haven't set up an Angular project for further testing to see what would happen on a downstream project. Do you expect a different behaviour (ie. something other than inclusion of Maybe it's a good time to start thinking of ugrading briangonzalez/jquery.adaptive-backgrounds.js to the next version. |
And the image i try to process is not so big. Happy new year to you! |
@AlfredJKwack On second glance, you're right. |
I think this issue is the most pressing one before the next release. This library does indeed block the UI while doing its thing.
or both of them. |
I like #2 as it seems like the right tool for the job. rgbaster is already async because we use |
I mean by (1) doing the the work in async function getCounts(
data: Uint8ClampedArray,
ignore: string[],
pixelsPerChunk = 1000 // what's an ideal default ??
): Promise<[]> {
function getCountMap(index: number, countMap = {}, resolve: Function) {
let upperBoundary = Math.min(index + 4 * pixelsPerChunk, data.length)
for (
let i = index;
i < upperBoundary;
i += 4 /* 4 gives us r, g, b, and a*/
) {
let alpha: number = data[i + 3]
// skip FULLY transparent pixels
if (alpha === 0) continue
let rgbComponents: number[] = Array.from(data.subarray(i, i + 3))
// skip undefined data
if (rgbComponents.indexOf(undefined) !== -1) continue
let color: string =
alpha && alpha !== 255
? `rgba(${[...rgbComponents, alpha].join(',')})`
: `rgb(${rgbComponents.join(',')})`
// skip colors in the ignore list
if (ignore.indexOf(color) !== -1) continue
if (countMap[color]) countMap[color].count++
else countMap[color] = { color, count: 1 }
}
if (upperBoundary == data.length) resolve(countMap)
else {
// queue the proecessing of the next chunk as a macrotsk
// for the next tick of the event loop
setTimeout( // what about requestAnimationFrame instead ??
() => getCountMap(index + 4 * pixelsPerChunk, countMap, resolve),
0
)
}
}
const countMap = await new Promise(resolve => getCountMap(0, {}, resolve))
const counts = Object.values(countMap) as []
return counts.sort((a: any, b: any) => b.count - a.count)
} |
Hi, On the topic of chunking out the work. Rather than guessing how many to chunk at once, it's also possible to let elapsed time be the guide for each chunk and to let it process as many as it can in a given time interval. This somewhat automatically guarantees browser responsiveness regardless of how CPU intensive the iteration is. So, rather than passing in a chunk size, you can pass in a millisecond value. This of course does open the question of how many milliseconds to pick. On a typical laptop with a refresh rate of 60 Hz, each frame is on the screen for about 16-17 ms. Lets assume we take a timer of 10 ms. This delay means a roughly 66% chance of blocking the ui for a single screen refresh. Such a delay would not be noticeable under most conditions. If the page has lots of animation, a 10ms delay might be noticeable. A gamble but not a huge one IMHO. Here's an example: // last two args are optional
function processLargeArrayAsync(array, fn, maxTimePerChunk, context) {
context = context || window;
maxTimePerChunk = maxTimePerChunk || 10;
var index = 0;
function now() {
return new Date().getTime();
}
function doChunk() {
var startTime = now();
while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
// callback called with args (value, index, array)
fn.call(context, array[index], index, array);
++index;
}
if (index < array.length) {
// set Timeout for async iteration
setTimeout(doChunk, 0);
}
}
doChunk();
}
processLargeArrayAsync(veryLargeArray, myCallback); I think we need to profile this code a little more to start with. I'm thinking that a 4k image would be the worst thing someone might reasonably throw at this. That's about 35mio items in the array to process. |
the image maipulation is a CPU intensive task, and it already takes a lot of time, even when choking the main thread, I think, as @briangonzalez mentioned, the web workers option would be better, giving us the freedom to process every thing in one loop without worries.
Any thoughts ? |
I am trying to use analyze function at version 2 to get dominant color of an image, but the call blocks
my UI and make a long-time no response in browser. My code is below and runtime is Angular 7.
I am also trying to install rgbaster.js and import analyze from 'rgbaster.js', but it don't works. I learned that the version 2 of rgbaster was written in typescript, so i think there is no any problem to using this library in Angular framework. But I hava no idea about that the analyze function doesn't work and block ui Thread and raise a crash to browser.
Thanks for your any replies!
The text was updated successfully, but these errors were encountered: