Loop through specified area of pixels - php

I've made a search by color feature on my photo website. To do this, I looped through all the pixels in one image and recorded the top 10 in my database. The problem I have is that because my photos have people subjects, my searchers are really searching for colors of the subject, rather than the background (sky, paving, grass etc). I know this will be very hard to eliminate completely but I'm thinking I need to change the color slightly to give better results.
Please see this example of one photo:
The example on the left is what I was doing, collecting ALL pixels. Black and grays took up the top 3 colors in that selection, a shade of pink was 4th and navy blue from the background girl's dress was 5th - I only collect the top 5 colors and my main color wasn't really ranked that good. If I could change the way I loop through the pixels, to select only the colors in the green box (right), I think it would make a slight bit of difference in my collection. This is where I'm struggling to see the math.
I was doing this, to loop through all pixels:
$size = #getimagesize($imageFile);
for ($x = 0; $x < $size[0]; $x += $granularity) {
for($y = 0; $y < $size[1]; $y += $granularity) {
// do stuff here
}
But I need to change this to take roughly 5% off the top and bottom and roughly 8-10% of the sides and then loop through the new selection.
I did start this but then stopped because I wasn't sure about my calculations returning odd numbers or number likes 1.030044858.
This is how far I got and hoped somebody can help clean it up and get me over the line:
$granularity = 4;
$granularity = max(1, abs((int)$granularity));
$size = #getimagesize($imageFile);
$sizeX = $size[0];
$sizeY = $size[1];
$xPerCent = 8; // for me to set & adjust
$yPerCent = 5; // for me to set & adjust
$xSelectionWidth = ($sizeX/100) * $xPerCent;
$xSelectionWidth = $sizeX - ($xSelectionWidth * 2);
$xSelectionHeight = ($sizeY/100) * $yPerCent;
$xSelectionHeight = $sizeY - ($ySelectionHeight * 2);
$xSelectionStart = 0;
$xSelectionEnd = 0;
$ySelectionStart = 0;
$ySelectionEnd = 0;
for($x = 0; $x < $size[0]; $x += $granularity) {
for($y = 0; $y < $size[1]; $y += $granularity) {
// do stuff here
}
}

If you want to ignore something in a picture, I suggest you to convert it to B&W and take the biggest white area (you will need to set details of the convertion to be sure you do not get all non already white color as black).
I mean red, and skin color should be set as white and violet as black. In this case, the background and any dark surface would be black. Read http://www.switchonthecode.com/tutorials/csharp-tutorial-convert-a-color-image-to-grayscale its a great introduction but, I am talking about real Black and white so, you may adjust/adapt this one.
This will greatly help you to get focus on the right part of the image.
The more accurate the B&W form recognition, the better you can identify the subject you wat color from. Please read http://en.wikipedia.org/wiki/Pattern_recognition for detailed infos on pattern recognition.
Get the set of coordinates you want to look at in the original image.
Sory I am not a PHP person, I can not be more specific in this way but, I hope this will help you...

Related

Crop/cut-out image based on existing selection/path already drawn on image (ideally via PHP)

I've scoured around the internet a fair bit and I can't seem to find any reference to what I am attempting to achieve... I fear that means I'm probably going about doing something the wrong way, but I'll pose this question here anyways in hopes that maybe I am not.
I would like to take an already generated image that has a rectangular selection already drawn on it via a specific color and a dynamic (but always rectangular) path, and crop or cut-out (and use) the inner area of that rectangular path.
Let's use an image generated by google maps as an example for this:
I thought perhaps the imagemagick library would hold a solution for this, but, I don't know if it's because I haven't quite narrowed down the exact key terms for what I am looking to do exactly, or if it's because it cannot (at least not simply) be done, but I haven't turned up any solutions.
Any solutions, advice, or smacks to the head are welcome.
[Please note that (for now) I would like to operate under the assumption that these images already exist, so any information regarding the pixel coordinates of the relative selection area on the image doesn't exist]
Your problem seems to boil down to this: How do I find a red rectangle in an image?
This is quite an open-ended problem, and could actually be quite difficult to solve. However, if the following assumptions can be made, then the task will be a lot easier:
The rectangle is drawn in pure RGB red (#ff0000).
The rectangle is aligned parallel with the image edges.
The image is saved in a lossless format like PNG.
The image contains no other pixels of this exact colour.
We know the width of the rectangle's edges.
The example you provided seems to tick all these boxes. Since it's stored as an 8-bit indexed color image, the first step would be to convert it into a true color image. This makes it easier to check the pixel values.
Then find the outermost edges of the frame, inset the coordinates by the frame width, and crop the image. Here's some code that will do this for you:
<?php
$src_img = 'er7RT.png';
$frame_color = 0xff0000;
$frame_width = 6;
// Load image and copy to true color image resource
$im = imagecreatefrompng($src_img);
$sw = imagesx($im);
$sh = imagesy($im);
$im1 = imagecreatetruecolor($sw, $sh);
imagecopy ($im1, $im, 0, 0, 0, 0, $sw, $sh);
imagedestroy($im);
// Get outer dimensions of frame.
// Assume the frame color appears nowhere else in the image.
$minx = $miny = 999999;
$maxx = $maxy = -$minx;
for ($x=0; $x<$sw; $x++) for ($y=$sh/20; $y<$sh; $y+=$sh/10) {
if (imagecolorat($im1,$x,$y)==$frame_color) { $minx = $x; break 2; }
}
for ($x=$sw-1; $x>=0; $x--) for ($y=$sh/20; $y<$sh; $y+=$sh/10) {
if (imagecolorat($im1,$x,$y)==$frame_color) { $maxx = $x; break 2; }
}
for ($y=0; $y<$sh; $y++) for ($x=$sw/20; $x<$sw; $x+=$sw/10) {
if (imagecolorat($im1,$x,$y)==$frame_color) { $miny = $y; break 2; }
}
for ($y=$sh-1; $y>=0; $y--) for ($x=$sw/20; $x<$sw; $x+=$sw/10) {
if (imagecolorat($im1,$x,$y)==$frame_color) { $maxy = $y; break 2; }
}
if ($minx>=$maxx || $miny>=$maxy) die("Couldn't locate frame");
// Subtract frame width to obtain crop region
$minx += $frame_width;
$maxx -= $frame_width;
$miny += $frame_width;
$maxy -= $frame_width;
// Create new image with cropped dimensions
$im2 = imagecreatetruecolor($maxx-$minx, $maxy-$miny);
imagecopy ($im2, $im1, 0, 0, $minx, $miny, $maxx-$minx, $maxy-$miny);
// Finish up
header("Content-Type: image/png");
imagepng($im2);
imagedestroy($im1);
imagedestroy($im2);

PHP: Function to classify html hex colours into a couple of simple string values

Is it possible to classify html hex colours into simple string values??
For example, the colour #CC3333, it's not completely red, but as a human we can assume that it's red. The colour #CCCCCC can be classified as white, because I don't want black or grey involved.
Possible simple values at least consist of:
red
white
green
More classification is better, but I want at least these colours.
Can it be done?
Optional info:
I'm creating a web apps that capture the pictures through webcam. The user can hold a white or red paper to the webcam and the apps detect the image's major colour. Then the user will be redirected to a different options depending on their colour. I've done the colour detection, but I just want classify it into several colour only, red,white,and green.
This is a somewhat subjective question, because yes it can be done, but exactly how you would do it would depend on your specific application - colour itself is very subjective in the way that it is observed by the individual.
You would need to start by splitting the string into it's red, green and blue components:
$colourId = 'CC3333';
list($red, $green, $blue) = str_split($colourId, 2);
Then, it would probably be an idea to convert them to integers:
$red = hexdec($red);
$green = hexdec($green);
$blue = hexdec($blue);
Then you need to apply some sort of logic to it to determine which one of your classes it falls into. How you do this is really up to you, but maybe you could do something like this:
if (max($red, $green, $blue) - min($red, $green, $blue) < 10) {
// If the values are all within a range of 10, we'll call it white
$class = 'white';
} else if (max($red, $green, $blue) == $red) {
// If red is the strongest, call it red
$class = 'red';
} else if (max($red, $green, $blue) == $green) {
// If green is the strongest, call it green
$class = 'green';
} else if (max($red, $green, $blue) == $blue) {
// If blue is the strongest, call it blue
$class = 'blue';
}
it's hard to classified the color in RGB model it's better convert the color to HSL or HSV model , and then you can classified the color. for more information you can look at:http://en.wikipedia.org/wiki/Color_model
You first need to convert the hex format to rgb values. A simple Google search turned up this page. I haven't tested it, but if it doesn't work correctly then I'm sure you can find a different one that does.
Once you have the rgb values, you need to define your color ranges. The following code creates color ranges at every interval of 63.75 (that's 4 ranges per color, so 4*4*4 = 64 total ranges):
function findColorRange($colorArray){
//assume $colorArray has the format [r,g,b], where r, g, and b are numbers in the range 0 - 255
for($i = 0; $i < 256; $i += 51){ //find red range first
if($colorArray[0] <= $i + 51/2 && $colorArray[0] >= $i - 51/2){
for($n = 51; $n < 256; $n += 51){ //green
if($colorArray[1] <= $n + 51/2 && $colorArray[1] >= $n - 51/2){
for($z = 51; $z < 256; $z += 51){ //blue
if($colorArray[2] <= $z + 51/2 && $colorArray[2] >= $z - 51/2){
return array($i,$n,$z);
}
}
}
}
}
}
}
The above function would return an array that defines the color range of the color in question. From there, you could map the possible ranges to whatever strings you want. This would probably be most easily achieved by creating an associative array where the keys are the r,g,b values, and the values are the string. For example:
$colorMap = array(
'0,0,0' => 'white',
'51,0,0' => 'light gray'
)

Crop darkspace from photo in PHP

This post is an extension of a question asked a couple years ago here:
Crop whitespace from image in PHP
That post does a magnificent job of explaining how to use php to crop pure white space from around an image. The issue here is that it expects "pure white" around the image. Here is what i am trying to do and how it differs:
I am using my cell phone as a "scanner" to take pictures of documents and then upload them to my server (don't worry...there is no "secret agent" stuff going on here...it is actually mostly receipts from lunches and sometimes business cards). In my case I always try to use a dark background to contrast with the light color paper. The images never come out with "pure" black/white colors however. My image is always in JPG format. I am experimenting with the code referenced above (but swapped to test for dark/black border) and wonder if there is a way to generalize the function to apply more broadly? I can install ImageMagick and use trimImage and some other stuff, but would rather stick to GD since that will be more useful to more StackOverflow users. Here is what i thought of so far:
IDEA #1: convert the image to greyscale and crank the contrast very high...do the comparison there (presumable against pure black at that point)...then use the found coordinates on the original (non-greyscale) image. What do you think...good approach? Would it work?
I would use imagefilter($img, IMG_FILTER_GRAYSCALE); and then imagefilter($img, IMG_FILTER_CONTRAST, -90);
IDEA #2: Go generic. This is the idea I need the most help with. It would be really cool if i could automatically detect the approximate range of colors from the border. The idea would be to sample a pixel (or several) from each of the four corners and then "average" them to figure out the background. This would mean that one day if i wanted to take the photo against a brown background, or a red background, or a green background...no problem. I think that the first step in this direction would involve the use of imagecolorclosest() but frankly am in WAY over my head on how to select, compare, average, and then re-compare the colors. Ideally there would be a way to just plug in some of this logic to the original function (from link above) and it would then be able to work generically regardless of what background color is in the image (assuming it is reasonably consistent) and would work with a photo (thus an image where the background color is not absolutely the same color value all the way across).
So...two part question: Is my IDEA #1 the best approach and are there any problems i should be aware of or suggestions on improvement/implementation? And...does anyone want to give a go at IDEA #2 and at least give me enough code to get moving in the right direction?
So i went ahead and played around with IDEA#1 and it is working exceptionally well. I am all set and need no further help at the moment...though still think that IDEA #2 is a better all-around solution, so if anyone wants to provide some pointers for it, i will play around with the ideas and try to come up with additional code examples to post here. For now, here is my working version of IDEA#1 as described above and tested on several cell-phone images:
//MANY THANKS TO: http://stackoverflow.com/questions/1669683/crop-whitespace-from-image-in-php
$OriginalImageFileLocation = 'sampleimage.jpg';
//load the image into a variable (and throw an error if image does not exist)
if( !$img = imagecreatefromjpeg("$OriginalImageFileLocation") )
{ exit("Could not use image $OriginalImageFileLocation"); }
// This function only can detect the background if it is "pure black"...so convert to greyscale then crank the contrast
imagefilter($img, IMG_FILTER_GRAYSCALE);
imagefilter($img, IMG_FILTER_CONTRAST, -100);
//find the size of the borders
$b_top = 0;
$b_btm = 0;
$b_lft = 0;
$b_rt = 0;
//top
for(; $b_top < imagesy($img); ++$b_top) {
for($x = 0; $x < imagesx($img); ++$x) {
if(imagecolorat($img, $x, $b_top) != 0x000000) {
break 2; //out of the 'top' loop
}
}
}
//bottom
for(; $b_btm < imagesy($img); ++$b_btm) {
for($x = 0; $x < imagesx($img); ++$x) {
if(imagecolorat($img, $x, imagesy($img) - $b_btm-1) != 0x000000) {
break 2; //out of the 'bottom' loop
}
}
}
//left
for(; $b_lft < imagesx($img); ++$b_lft) {
for($y = 0; $y < imagesy($img); ++$y) {
if(imagecolorat($img, $b_lft, $y) != 0x000000) {
break 2; //out of the 'left' loop
}
}
}
//right
for(; $b_rt < imagesx($img); ++$b_rt) {
for($y = 0; $y < imagesy($img); ++$y) {
if(imagecolorat($img, imagesx($img) - $b_rt-1, $y) != 0x000000) {
break 2; //out of the 'right' loop
}
}
}
//now re-initialize the original image since the greyscaled version was just for gathering cordinates
$img = imagecreatefromjpeg("$OriginalImageFileLocation");
// OPTIONAL: make the final image a bit prettier (and greyscale for long-term consistency)
imagefilter($img, IMG_FILTER_GRAYSCALE);
imagefilter($img, IMG_FILTER_BRIGHTNESS, +15);
imagefilter($img, IMG_FILTER_CONTRAST, -20);
//copy the contents, excluding the border
$newimg = imagecreatetruecolor( imagesx($img)-($b_lft+$b_rt), imagesy($img)-($b_top+$b_btm) );
imagecopy($newimg, $img, 0, 0, $b_lft, $b_top, imagesx($newimg), imagesy($newimg));
//finally, output the image
header("Content-Type: image/jpeg");
imagejpeg($newimg);

Detecting blank generated images with php?

How would I be able to detect that an image is blank (only of a single, arbitrary color or, with a gif, frames of random arbitrary colors) using PHP and/or imagemagick?
I think this is what I'm going to try:
http://www.php.net/manual/en/function.imagecolorat.php#97957
You can check the image inside of PHP using imagecolorat (this may be slow, but it works):
function isPngValidButBlank($filename) {
$img = imagecreatefrompng($filename);
if(!$img)
return false;
$width = imagesx($img);
$height = imagesy($img);
if(!$width || !$height)
return false;
$firstcolor = imagecolorat($img, 0, 0);
for($i = 0; $i < $width; $i++) {
for($j = 0; $j < $height; $j++) {
$color = imagecolorat($img, $i, $j);
if($color != $firstcolor)
return false;
}
}
return true;
}
http://www.php.net/manual/en/function.imagecolorstotal.php gives you the amount of colors in an image. Hmm, in my demo it doesn't seem to work, sorry :( an image i created (fully red, 20x20 pixels) gives 0 colors for PNG and 3 colors for GIF.
Ok: http://www.dynamicdrive.com/forums/showpost.php?p=161187&postcount=2 look at the 2nd piece of code. Tested here: http://www.pendemo.nl/totalcolors.php
Kevin's solution can be sped up using random sampling. If you have some idea of the percentage of pixels that should be different from the background (assuming you aren't dealing with lots of images with only 1 different pixel), you can use the Poisson distribution:
probability of finding a nonblank pixel = 1 - e^(-n*p)
where n is the number of samples to try, and p is the percentage of pixels expected to be nonblank. Solve for n to get the appropriate number of samples to try:
n = -log(1 - x) / p
where x is the desired probability and log is natural log. For example, if you are reasonably sure that 0.1% of the image should be nonblank, and you want to have a 99.99% chance of finding at least one nonblank pixel,
n = -log(1-.9999)/.001 = 9210 samples needed.
Much faster than checking every pixel. To be 100% sure, you can always go back and check all of them if the sampling doesn't find any.
For anyone using Imagick to try and achieve this, the getImageColors() method did the trick.
https://www.php.net/manual/en/imagick.getimagecolors.php
$img = new Imagick();
$img->readImage($image);
$colors = $img->getImageColors();
if(!$colors > 1) {
return false;
}
Get the standard-deviation from the verbose statistics for each tile. If the standard deviation is 0, then the image is one color.
Supposedly, 'number of colors' will also do this; would be 1.
Use the -format option: http://www.imagemagick.org/script/escape.php

Generate colour palette from an image

Just for fun I've been looking at how to use the GD library to create a colour palette from an image. So far I've used GD to resize a user uploaded image to an appropriate size for displaying on a webpage.
Now I'd like to be able to get about five or so different colours from the image that represent the range of colours present in it. Once I've done that I'd like to generate a complementary palette based upon those colours, which I can then use to colour different elements on the page.
Any help I can get about how I would find the initial colour palette would be much appreciated!
EDIT:
I've come to my own solution which you can see below.
Well I've spent a couple of days fiddling around and this is how I managed to build my colour palette. Its worked fairly well for me and you can change the size of the colour palette to return more or less colours from the image.
// The function takes in an image resource (the result from one
// of the GD imagecreate... functions) as well as a width and
// height for the size of colour palette you wish to create.
// This defaults to a 3x3, 9 block palette.
function build_palette($img_resource, $palette_w = 3, $palette_h = 3) {
$width = imagesx($img_resource);
$height = imagesy($img_resource);
// Calculate the width and height of each palette block
// based upon the size of the input image and the number
// of blocks.
$block_w = round($width / $palette_w);
$block_h = round($height / $palette_h);
for($y = 0; $y < $palette_h; $y++) {
for($x = 0; $x < $palette_w; $x++) {
// Calculate where to take an image sample from the soruce image.
$block_start_x = ($x * $block_w);
$block_start_y = ($y * $block_h);
// Create a blank 1x1 image into which we will copy
// the image sample.
$block = imagecreatetruecolor(1, 1);
imagecopyresampled($block, $img_resource, 0, 0, $block_start_x, $block_start_y, 1, 1, $block_w, $block_h);
// Convert the block to a palette image of just one colour.
imagetruecolortopalette($block, true, 1);
// Find the RGB value of the block's colour and save it
// to an array.
$colour_index = imagecolorat($block, 0, 0);
$rgb = imagecolorsforindex($block, $colour_index);
$colour_array[$x][$y]['r'] = $rgb['red'];
$colour_array[$x][$y]['g'] = $rgb['green'];
$colour_array[$x][$y]['b'] = $rgb['blue'];
imagedestroy($block);
}
}
imagedestroy($img_resource);
return $colour_array;
}
this may help you
<?php
$im = ImageCreateFromJpeg($source_file);
$imgw = imagesx($im);
$imgh = imagesy($im);
// n = total number or pixels
$n = $imgw*$imgh;
$colors = array();
for ($i=0; $i<$imgw; $i++)
{
for ($j=0; $j<$imgh; $j++)
{
$rgb = ImageColorAt($im, $i, $j);
if (isset($colors[$rgb])) {
$colors[$rgb]++;
}
else {
$colors[$rgb] = 1;
}
}
}
asort($colors);
print_r($colors);
The imagecolorat function will give you the colour value at a pixel which you can use to scan the image and create a colour histogram:
http://www.php.net/manual/en/function.imagecolorat.php

Categories