I need a really fast method of detecting white space around an image, specifically I need the coordinates of where the first non-white pixel begins for each side (e.g. left, top, right, bottom.)
ImageMagick is too slow, so is looping through each pixel along each side with GD and seeing if it's white. I've got to do about 500,000,000 images so every microsecond makes a difference.
BTW, the image is black & white only.
If there is an external app that can do this, which I can exec with PHP, then that would be fine.
is there any extra information you know about the images that can be used to help?
Like do the images start white then go black then stay black? or can any pixel be white or black and the fact that any one is white or black doesn't tell you anything about the others?
If any pixel can be white or black regardless of the other pixels then I don't see how you can do much better than checking each pixel in a loop until you find the first non white one...
If you know that if the fifth pixel from the left is white then 0-4 are definitely white as well then you might be able to check fewer pixels instead by using some sort of modified binary type search (as you could skip checking 0-4 in this case and just check say 5, then 10 and if 5 is white and 10 is black you know the point is somewhere between 5-10, so then you can split the difference and check 7 etc etc until you find the point at which they change.)
I think you might end up with a trade off between speed and accuracy here. The most accurate way is to slice through each column and row, starting at the extremities, checking each pixel. Once you find a hit in a column you have found the edge on one side. This could be done in parallel as each check is independent. you might be able to speed this up by, as you said, checking only every nth pixel, but this is likely to come a cropper occasionally, especially with such a large data set. this may or may not be acceptable. you may be able to improve this by checking around the area you find a match to check that the match is accurate. So if you check every 3rd pixel and you find a hit at pixel 15 then check 14 to see it if is a hit (and 13 if 14 is). using this you might be able to get away with fewer checks.
One algorithm that could work if you have a mostly continuos border of darker pixels:
Left side:
Take the middle pixel and start checking the pixels to the right until you find a black one.
Then move from there up/down until you hit a black pixel
When you find a black pixel move to the left until you hit a white one
Repeat 2,3 until you end up at top/bottom
That of course will not work if there are gaps, like in text.
Related
I am creating a data entry web application using phpOCR. Where user need to put the numbers that will show on image. In that purpose I am using phpOCR, but it only detect the numbers that is very clean, but I need to detect the numbers that is blur on the image.
The below image is an example of my images, where phpOCR detects only the numbers in red, not the blur numbers.
On the below image the blur number is 10-30-60.
Is there any way to solve this?
You might be able to drastically change the contrast and brightness of the image - then, possibly, those faint letters will become black, and the background completely white. The OCR may then be able to read it. But then, some things may just not be possible for the OCR to detect.
I've got a security camera set up, and i'm comparing 1 minute batches of images to check if there's been any motion. I have 10 coordinates that I check between each image. If any pixels don't match the previous image, it triggers a warning message.
Problem is, it works too well.
The logic is basically if imagecolorat() is greater or less than a 10% difference from the previous imagecolorat(), it triggers. So, if a cloud comes over the house, it triggers. Basically any change in light triggers it. So, I've moved the threshold from 10% to 30% and it triggers less but I'm worried now that if I move any past that, real motion wont be detected.
Note: I'm using the raw output of imagecolorat(), not the RGB values. I'm not sure if this would have an impact.
You are looking for larger discontinuities - things like noise and slow variation should be discounted (unless this motion detection is for very slow moving small things, like garden gnomes).
Therefore, do a histogram equalize or something similar before image subtraction to account for global shifts in light, and do some filtering / edge enhancement before differencing to enhance the changes. Using more of the image will be better I would think, than just 10 points.
Histogram equalization entails looping through the image, and counting bins for each brightness value - so you have a resulting data set that says how many pixels are in a set of tonal ranges. In other words, say you divvy up into 16 bins - pixels that have a greyscale value (or alternately, the Brightness in an HSB model) in the value of 0..15 (assuming an 8 bit image here in this channel) are all in bin 1. Then you go forth and compute a series of linear stretches to apply to each bin so that it occupies an output range in proportion to its population. So if ALL of your pixels are in the 0.. 15 bin, you would just multiply everything by 16 to stretch them out. The goal is that your histogram of your equalized image is flat - equal amounts of pixel in every bin.
Edge enhancement can be simply done with the application of a Sobel filter.
We have this map, we need to use PHP to take all the shades of blue out, as well as the percentages. The problem is, is that some of the percentages are in the same color as the borders, and other times, the percentages go into the border. We need to use this image.
There are not (AFAIK) really easy ways.
The easiest way doesn't give you good results: separate channels and delete small components.
The result is this:
As you can see there are a few numbers and percent signs remaining because they are connected to the delimiting lines and deleting small components doesn't work for them.
If you need a better job, you should correlate the image with a template of each number, and once identified, delete it.
Here you can see the result of the correlation with the number "2":
One wrong "2" is identified, (see top left), so a more sophisticated approach may be needed for a general procedure.
Anyway, I think these kind of manipulation is well beyond what you can expect from K-12.
HTH!
Edit
As per your request, some detail about the first method.
You first separate the three channels, and get three images:
You keep the third (the blue channel)
Then you need to delete the smaller components. There are a lot of methods to do that, probably the easiest is derived from the connectivity detection for example in the flood-fill algorithm, but you just measure the components without filling them.
The basic (not optimized) idea is to travel every pixel in the image and count how many pixels are "connected" with it. If there are less than a specified number (threshold), you just delete the whole set. Most image manipulation libraries have all these functions already implemented.
For this specific image, if you open the image in image editing software, convert the mode from index to true color (RGB), and then color dodge the entire image with yellow (RGB: 255,255,0), you wind up with a black and white image consisting of the outlines and numbers. (this is also what the blue channel looks like BTW)
So either throw away the red and green channels, or implement a color dodge algorithm.
Another alternative is to sample each pixel, and the set that pixel's R & G components to the B value
edit: actually, I forgot about the white numbers. to get those, flood fille the outer white with the rgb(0,0,255), invert the entire image, and color dodge with (255,255,0), the red or green channel is now the missing numbers. Overlay these on top of the processed image from previous steps above.
Getting rid of the shaded colors should be easy.
Getting rid of the numbers is more tricky. I would:
Make a lookup table of the pixel data associated with each number and the % sign.
When clearing an area, look for the numbers (black or white) and only clear out exact patterns from the lookup table.
Recreate the border between areas by adding a black color between different shades.
It's impossible to do this with guaranteed accuracy simply because the digits hide original information. However, I think the above steps would give you close to 100% accuracy without a lot of effort.
What's the best approach to comparing two images with php and the Graphic Draw (GD) Library?
This is the scenario:
I have an image, and I want to find which image of a given set is the most similar to it.
The most similar image is in fact the same image, not pixel perfect match but the same image.
I've dramatised the difference between the two images with the number one on the example just to ease the understanding of what I meant.
Even though it brought no consistent results, my approach was to reduce the images to 1px using the imagecopyresampled function and see how close the RGB values where between images.
The sum of the values of deducting each red, green and blue decimal equivalent value from the red, green and blue decimal equivalent value of the possible match gave me a dissimilarity index that, even though it didn't work as expected since not always the most RGB similar image was the target image, I could use to select an image from the available targets.
Here's a sample of the output when comparing 4 images against a target image, in this case the apple logo, that matches one of them but is not exactly the same:
Original image:
Red:222 Green:226 Blue:232
Compared against:
http://a1.twimg.com/profile_images/571171388/logo-twitter_normal.png
Red:183 Green:212 Blue:212 and an index of similarity of 56
Red:117 Green:028 Blue:028 and an index of dissimilarity 530
Red:218 Green:221 Blue:221 and an index of dissimilarity 13 Matched Correctly.
Red:061 Green:063 Blue:063 and an index of dissimilarity 491
May not even be doable better with better results than what I'm already getting and I'm wasting my time here but since there seems to be a lot of experienced php programmers I guess you can point me in the right directions on how to improve this.
I'm open to other image libraries such as iMagick, Gmagick or Cairo for php but I'd prefer to avoid using other languages than php.
Thanks in advance.
I'd have thought your approach seems reasonable, but reducing an entire image to 1x1 pixel in size is probably a step too far.
However, if you converted each image to the same size and then computed the average colour in each 16x16 (or 32x32, 64x64, etc. depending on how much processing time/power you wish to use) cell you should be able to form some kind of sensible(-ish) comparison.
I would suggest, like middaparka, that you do not downsample to a 1 pixel only image, because you loose all the spatial information. Downsampling to 16x16 (or 32x32, etc.) would certainly provide better results.
Then it also depends on whether color information is important or not to you. From what I understand you could actually do without it and compute a gray-level image starting from your color image (e.g. luma) and compute the cross-correlation. If, like you said, there is a couple of images that matches exactly (except for color information) this should give you a pretty good reliability.
I used the ideas of scaling, downsampling and gray-level mentioned in the question and answers, to apply a Mean Squared Error between the pixels channels values for 2 images, using GD Library.
The code is in this answer, including a test with those ideas.
Also I did some benckmarking and I think the downsampling could be not needed in those little images, cause the method is fast (being PHP), just a fraction of a second.
Using middparka's methods, you can transform each image into a sequence of numeric values and then use the Levenshtein algorithm to find the closest match.
I've used GD before, but only ever for resizing/generating images on the fly - though I'm pretty positive it has the capabilities to do what I'm after.
As simply as possible, I need to check an image to find out whether it has a light background or a dark background. I.e. if the background is predominately 'light' I'm returned a value of '1', and if it is predominately 'dark' it returns '0'.
There's only going to be 5 images iterated through in this process at a time, but I'm very concious here of processing time - the page is going to be called often.
Can anyone point me in the right direction on where to go with this?
First see if there are any patterns you can take advantage of - for instance, is the top-left or top-right corner (for example) always going to be of the background colour? If so, just look at the colour of that pixel.
Maybe you can get a "good enough" idea by looking at some key pixels and averaging them.
Failing something simple like that, the work you need to do starts to rise by orders of magnitude.
One nice idea I had would be to take the strip of pixels going diagonally across from the top-left corner to the bottom-right corner (maybe have a look at Bresenham's line algorithm). Look for runs of dark and light colour, and probably take the longest run; if that doesn't work, maybe you should "score" runs based on how light and dark they are.
If your image is unnecessarily large (say 1000x1000 or more) then use imagecopyresized to cheaply scale it down to something reasonable (say 80x80).
Something that will work if MOST of the image is background-colour is to resample the image to 1 pixel and check the colour of that pixel (or maybe something small, 4x4 or so, after which you count up pixels to see if the image is predominantly light or dark).
Note that imagecopyresampled is considerably more expensive than imagecopyresized, since 'resized just takes individual pixels from the original whereas 'resampled actually blends the pixels together.
If you want a measure of "lightness" you could simply add the R, G and B values together. Or you could go for the formula for luma used in YCbCr:
Y' = 0.299 * R + 0.587 * G + 0.114 * B
This gives a more "human-centric" measure of lightness.