I have a library of like 1 million images, and roughly half of these are watermarked with the same, half transparent watermark in the same spot.
Where do I begin, detecting the images with the watermarks? Is there some standard tools for this purpose?
If according to your question, you just want to detect the images that are watermarked, you can use the following algorithm:
Extract a sample of the watermarking image Scan the watermark image
pixel by pixel and store the first pixels in an array. Scan each
image pixel by pixel and store in an array. Whenever a row matrix
from the image being scanned contains elements of the array in the
same order, it's most likely a match.
The code could be something like this:
$no_of_pixels = what_you_got;
$matched = 0;
$thumbpixels = array();
$wmark = imagecreatefrompng("watermark.png");
list($width, $height) = getimagesize("watermark.png");
$tesimage = imagecreatefrompng("test.png");
for($h = 0; $h < $height; $h++){
for($w = 0; $w < $width; $w++){
if(imagecolorsforindex($testimage, imagecolorat($testimage, $w, $h)) == $thumbpixels[0]){
while($thumbpixels[$i++] === imagecolorsforindex($tesimage, imagecolorat($wmark, $w, $h)) && $no_of_pixels != $matched){
$matched++;
}
if($matched == $no_of_pixels) echo "Voila, we found it!";
}
}
}
EDIT
Just seeing your thumbnail example. If you just want to detect text, you can try tesseract-ocr or PhpOCR.
You may also consider PHPSane
Detecting almost any feature in an image is called Object Detection. There is a widely used libray called OpenCV. It has a very simple SDK, although setting up can be a real pain. It is well supported for C/C++ and (nearly well supported for) Python. It took me arnd 3 weeks to train my own Classfier (training), first time I started using OpenCV.
But I would not really depend on this solution entirely and consider my priorities. Also, it is very hard to achieve good rate with custom classifier. Other methods are more time consuming.
In short, not with complete accuracy.
At best, you could only apply heuristics on the image to see if it matches an exact watermark, and get a confidence rating -- for example, if the watermark if a 50% white overlay, then a scene that was predominantly white could give a false positive, and of course the inverse is true.
There are also problems that could arise if the images use a lossy compression, such as JPEG, as the edges, and the saturation may result in a watermark that isn't as saturated as expected, or as exactly positioned as expected.
Because you know where the watermark always is it is possible that you could use imagecolorat and imagecolorsforindex to get the alpha value for pixels both inside and outside of the watermark. I would expect the alpha values to be similar when there is no watermark, and different when there is (within some threshold that you would need to determine). Of course, this may not work on all images so if you need 100% accuracy you would probably need something more reliable.
In your case, where you are looking for the same logo in a predictable location, it is relatively simple. However it's much, much simpler and faster (as per my comment elsewhere) to match a copyright notice in the meta data!
A watermark is not going to produce fixed changes to the content - each modified pixel will obtain a new value based on the watermark and the image itself. Hence you need to extract this information - I'd go with diferentiating the image and just looking at the magnitude of the derivative (not the phase).
Then it's simply a matter of correlating the differential with one of just the watermark (or lots with the watermark and other content).
You really don't want to be doing this kind of image processing in PHP unless you're happy writing your own extensions. Most image processing toolkits will support differentiation and correlation.
BTW: if you don't know how to differentiate an image, and/or can't understand how to correlate an image, please don't ask - this is not the right forum for that discussion
Well if there is no tool to do this, you could try the following:
identify where the watermark appears as a percentage of pixels, eg bottom right 40px x 100px
For each image, make a temp copy and crop out the location of where the watermark would appear. This should leave both the watermarked version and the non watermarked version as the same
compare the images - e.g. combination of width x height, filesize, CRC or actual pixel comparison, though for a million images you'd need some serious CPU power.
Related
Aim: Find the white patch with the biggest area. And if possible then confirm if it is a circle. Then find the average centre of this patch and radius.
I have thought of creating an edge around the images, but I don't know how that would help in finding out the area of the patches.
I am trying to accomplish this and if anyone can guide me in the right direction, I would really appreciate it.
I am currently using php.gd library and that is the only tool I have.
Thanks
Detecting edges in images is actually quite simple. The most standard way is to use an image convolution operation and choosing the right kernel to sweep over your image. There is a function in php.gd called imageconvolution where you can specify an input image, a convolution kernel, a divisor and an offset. The divisor simply divides all values in the kernel by this value and the offset adds a value to each value in the kernel. As such, assuming that your image is loaded into the array $im, you would do this:
$edgeMatrix = array(-1,-1,-1,-1,8,-1,-1,-1,-1);
$divisor = 0;
$offset = 0;
imageconvolution($im, $edgeMatrix, $divisor, $offset);
Note that imageconvolution mutates the original input image, so $im will contain the edge detected result. Make sure that you have a copy of the original image somewhere in case you need it.
$edgeMatrix = array(-1,-1,-1,-1,8,-1,-1,-1,-1); is the standard Laplacian convolution matrix and is used quite often for edge detection tasks.
However, there is a function called imagefilter where there is an edge detect flag - IMG_FILTER_EDGEDETECT. This function filters an image given one of several options, and edge detection is one of these options. As such, you would do this:
imagefilter($im, IMG_FILTER_EDGEDETECT);
As with imageconvolution, imagefilter also mutates the input image, so again make sure that you keep the original image somewhere if you need it.
Hopefully one of these will work for you!
It sounds harder than it really is, and I can program this, though I'm searching for shortcuts.
I have a transparent PNG, with the dimensions of 250x525 pixels. Inside is a character that can be customized by my users, thus the dimensions of the real content inside that 250x525 image can vary. I want to reduce the 250x525 image down to just the avatar, by detecting where the first pixel begins vertically and horizontally, and where the last pixel ends.
Is there a shortcut for this? A function, or another way to do it perhaps? An example of an image I'd like to apply this to: http://i.troll.ws/92f2db20.png - imagine a box around the character. I want to calculate an invisible box around it, and pull the character out of it into its own image resource in GD.
The Reason: I have a function that can take an image and scale it to fit a larger image. I want to apply this to these characters, and first need to dumb it down to just the character.
Not GD, but Imagemagick/Imagick is able to do that vith the trimImage method.
$im = new Imagick("http://i.troll.ws/92f2db20.png");
$im->trimImage(0);
$im->writeImage('image.png');
I have an image which is essentially a star-burst effect. The color of the star-burst is white, and the background is transparent (PNG w/ Alpha). I randomly generate these star-bursts onto an HTML5 canvas at random locations, and at the same time, generate random Hue, Saturation, and Light (HSL) values. These could be RGB values, if this simplifies things.
The goal is to re-colorize the PNG for display on the HTML5 canvas based on the randomly generated HLS values before rendering it to the canvas.
I've read some other posts on StackOverflow and other sites, but the only solutions I've seen involve rendering it to the canvas, grabbing the coordinates for where the image is displaying, and modify the color on a pixel-by-pixel basis. In theory this could work, however some of the images may overlap slightly. Also if there is a background already present, then from what I understand, the background's color would also be modified which isn't much of a solutions for me either.
If this is out of the realm of what Canvases are capable of, as a fallback I suppose I would be okay with having images dynamically re-colored via PHP using GD2 or Imagick, or via the command-line via Gimp, ImageMagick or some other image library...
Thanks much!
-- OUTCOME --
Special thanks to #jing3142 for initial suggestion of off-screen canvas rendering, and #Jarrod for providing the vital piece I was missing: globalCompositeOperation = "source-in"
Here is a working implementation of the concept: http://jsfiddle.net/fwtW2/2/
Works in:
Chrome
Firefox
IE 9 (haven't tested other versions)
How about a second canvas that has the size of the image, place the image on that canvas, re-colour as required, extract re-coloured image and place at random on main canvas, re-colour image on second canvas using new HSL values, extract and randomly place on main canvas and repeat?
This may help as well https://developer.mozilla.org/en-US/docs/HTML/Canvas/Pixel_manipulation_with_canvas
I'm pretty sure you can just use the source-in globalCompositeOperation opertaion? No need to get all hardcore and crazy with vector images.
This code is where the magic happens:
var color = 'red';
ctx.fillStyle = color;
ctx.globalCompositeOperation = "source-in";
But yOu'd need to re-draw this to an offscreen canvas: You can do that like so
createBuffer = function(sizeW, sizeH)
{
buffer = document.createElement('buffer');
bufferCtx = buffer.getContext('2d');
buffer.width = sizeW;
buffer.height = sizeH;
}
then just draw your image to the offscreen canvas and apply the global composition.
Here’s how to easily recolor your starbursts just before rendering on canvas
Convert your starburst.png into a vector path! (I know, you will have to drag out Gimp/Illustrator for this one time—but it’s worth it!)
Starburst paths can have fills and the fills can be solid colors or even gradients—as fancy, random recoloring as you need!
Starburst paths can scale without the pixilation of raster images like .png’s.
Starburst paths can rotate to show diversity in your bursts--and even skew to represent a bit of 3d motion.
If you want to get fancy, you can even create “glowing” bursts by using shadows and blurs.
Starburst paths are faster to render than .png images—especially if you need to manipulate each .png before drawing.
I am working on a real estate website and i would like to write a program that
can figure out(classify) if an image is a floor plan or a company logo.
Since i am writing in php i will prefer a php solution but any c++ or opencv solution will be fine as well.
Floor Plan Sample:
alt text http://www.rentingtime.com/uploads/listing/l0050/0000050930/68614.jpg
alt text http://www.rentingtime.com/uploads/listing/l0031/0000031701/44199.jpg
Logo Sample:
alt text http://www.rentingtime.com/uploads/listing/l0091/0000091285/95205.jpg
As always, there is a built-in PHP function for this. Just joking. =)
All the floor plans I've seen they are pretty monochromatic, I think you can play with the number of colors and color saturation to have a pretty good guess is the image is a logo or a floor plan.
E.g.: is the image has less than 2 or 3 colors is a floor plan.
E.g.: if the sum / average of the saturation is less than X it's a floor plan.
Black and white (and other similar colors that are used in floor plans) have a saturation that is zero, or very close to zero, while logos tend to be more visually attractive, hence use more saturated colors.
Here is a simple function to compute the saturation of a Hex RGB color:
function Saturation($color)
{
$color = array_map('hexdec', str_split($color, 2));
if (max($color) > 0)
{
return (max($color) - min($color)) / max($color);
}
return 0;
}
var_dump(Saturation('000000')); // black 0.0000000000000000
var_dump(Saturation('FFFFFF')); // white 0.0000000000000000
var_dump(Saturation('818185')); // grey 0.0300751879699249
var_dump(Saturation('5B9058')); // green 0.3888888888888889
var_dump(Saturation('DE1C5F')); // pink 0.8738738738738738
var_dump(Saturation('FE7A15')); // orange 0.9173228346456692
var_dump(Saturation('FF0000')); // red 1.0000000000000000
var_dump(Saturation('80FF80')); // --- 0.4980392156862745
var_dump(Saturation('000080')); // --- 1.0000000000000000
Using imagecolorat() and imagecolorsforindex() you can implement a simple function that loops trough all the pixels of the image and sums / computes the average of the saturation. If the image has a saturation level above of a custom threshold you define you can assume that the image is a logo.
One thing you shouldn't forget is that images that have a higher resolution will normally have more saturation (more pixels to sum), so for the sake of this algorithm and also for the sake of your server performance it would be wise to resize all the images to a common resolution (say 100x100 or 50x50) to classify them and once classified you could use the original (non-resized) images.
I made a simple test with the images you provided, here is the code I used:
$images = array('./44199.jpg', './68614.jpg', './95205.jpg', './logo.png', './logo.gif');
foreach ($images as $image)
{
$sat = 0;
$image = ImageCreateFromString(file_get_contents($image));
for ($x = 0; $x < ImageSX($image); $x++)
{
for ($y = 0; $y < ImageSY($image); $y++)
{
$color = ImageColorsForIndex($image, ImageColorAt($image, $x, $y));
if (is_array($color) === true)
{
$sat += Saturation(dechex($color['red']) . dechex($color['green']) . dechex($color['blue']));
}
}
}
echo ($sat / (ImageSX($image) * ImageSY($image)));
echo '<hr />';
}
And here are the results:
green floor plant: 0.0151028053
black floor plant: 0.0000278867
black and white logo: 0.1245559912
stackoverflow logo: 0.0399864136
google logo: 0.1259357324
Using only these examples, I would say the image is a floor plant if the average saturation is less than 0.03 or 0.035, you can tweak it a little further by adding extra examples.
It may be easiest to outsource this to humans.
If you have a budget, consider Amazon's Mechanical Turk. See Wikipedia for a general description.
Alternatively, you could do the outsourcing yourself. Write a PHP script to display one of your images and prompt the user to sort it as either a "logo" our "floorplan." Once you have this running on a webserver, email your entire office ånd ask everyone to sort 20 images as a personal favor.
Better yet, make it a contest-- the person who sorts the most images will win an ipod!
Perhaps most simply, invite everyone you know over for pizza and beers and setup a bunch of laptops and get everyone to spend a few minutes sorting.
There are software ways to accomplish your task, but if it is a one-off event with less than a few thousand images and a budget of at least a few hundred dollars, than I think your life may be easier using humans.
One of the first things that comes to mind is the fact that floor plans tend to have considerably more lines oriented at 90 degrees than any normal logo would.
A fast first-pass would be to run Canny edge detection on the image and vote on angles using a Hough transform and the rho, Theta definition of a line. If you see a very strong correspondence for Theta=(0, 90, 180, 270) summed over rho, you can classify the image as a floor plan.
Another option would be to walk the edge image after the Canny step to only count votes from long, continuous line segments, removing noise.
I highly doubt any such tool already exists, and creating anything accurate would be non-trivial. If your need is to sort out a set of existing images ( e.g., you have an unsorted directory ), then you might be able to write a "good enough" tool and manually handle the failures. If you need to do this dynamically with new imagery, it's probably the wrong approach.
Were I to attempt this for the former case, I would probably look for something trivially different I can use as a proxy. Are floor plans typically a lot larger then logos ( in either file size or image dimensions )? Do floor plans have less colors then a logo? If I can get 75% accuracy using something trivial, it's probably the way to go.
Stuff like this - recoginition of patterns in images - tends to be horribly expensive in terms of time, horribly unreliable and in constant need of updating and patching to match new cases.
May I ask why you need to do this? Is there not a point in your website's workflow where it could be determined manually whether an image is a logo or a floor plan? Wouldn't it be easier to write an application that lets users determine which is which at the time of upload? Why is there a mixed set of data in the first place?
Despite thinking this is something that requires manual intervention, one thing you could do is check the size of the image.
A small (both in terms of MB and dimensions) image is likely to be a logo.
A large (both in terms of MB and dimensions) image is likely to be a floorplan.
However, this would only be a probability measurement and by no means foolproof.
The type of image is also an indicator, but less of one. Logos are more likely to be JPG, PNG or GIF, floorplans are possibly going to be TIFF or other lossless format - but that's no guarantee.
A simple no-brainer attempt I would first try would be to use SVM to learn the SIFT keypoints obtained from the samples. But before you can do that, you need to label a small subset of the images, giving it either -1 (a floor plan) or 1 (a logo). if an image has more keypoints classified as a floor plan then it must be a floorplan, if it has more keypoints classified as a logo then it must be a logo. In Computer Vision, this is known as the bag-of-features approach, also one of the simplest methods around. More complicated methods will likely yield better results, but this is a good start.
As others have said, such image recognition is usually horribly complex. Forget PHP.
However, looking over your samples I see a criteria that MIGHT work pretty well and would be pretty easy to implement if it did:
Run the image through good OCR, see what strings pop out. If you find a bunch of words that describe rooms or such features...
I'd rotate the image 90 degrees and try again to catch vertical labels.
Edit:
Since you say you tried it and it doesn't work maybe you need to clean out the clutter first. Slice the image up based on whitespace. Run the OCR against each sub-image in case it's getting messed up trying to parse the lines. You could test this manually using an image editor to slice it up.
Use both color saturation and image size (both suggested separately in previous answers). Use a large sample of human-classified figures and see how they plot in the 2-D space (size x saturation) then decide where to put the boundary. The boundary needs not be a straight line, but don't make too many twists trying to make all the dots fit, or you'll be "memoryzing" the sample at the expense of new data. Better to find a relatively simple boundary that fits most of the samples, and it should fit most of the data.
You have to tolerate a certain error. A foolproof solution to this is impossible. What if I choose a floorplan as my company's logo? (this is not a joke, it just happens to be funny)
How would you develop something similar to what is described in this DabbleDB blog post?
Just answered a kinda related SO question yesterday. Some of the concepts there, along with the resulting test code (on git hub) could be a good start.
As evolve mentions, scanning every pixel in an image (or even just the border) can be resource intensive. However, in this case (since you want to identify far more than just average color) it may be the way to go. Resizing the logo to a sane size would help reduce the server load, and shouldn't really effect the output.
Update: For these examples assume an image object has been created and $width and $height have been determined using imagesx(), getimagesize(), etc.
Background Color
The first thing we needed to do was figure out the logo’s background color. And that’s all the first version did, by using the corner pixels.
Here use imagecolorat() to find the corner colors. Alternatively, use the average border color method from the referenced answer at the top.
$color = imagecolorat($image, 0, 0); //do this for each corner
$rgb = imagecolorsforindex($image, $color); //convert each color to a RGB array
//average colors
Saturation
It turns out color theory has a way of measuring interestingness: saturation. So we pulled out an interesting color from the logo. Using the same color for the border and the text made things a bit more monotone than we wanted, so finally we decided to try and grab two interesting colors if they’re present.
You can use the RGB to HSL functions at the imagecolorsforindex() manual page along with the pixel scanning code mentioned at the top to find color values with high saturation.
Luminance
We turned again to color theory and realized we could use the border color’s luminance to decide whether black or white text was more suitable.
This SO thread lists different RGB to luminance calculations. I'm not certain what method is best (or technically correct) to convert 0-255 RGB images. But for what this is accomplishing (should the text be dark or light), I don't think it'll matter that much.