Generate colour palette from an image - php

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

Related

Drawing rectangles in php not working

I am creating an image in php and populating it with 10x10 pixel rectangles filled with a color.
$image = imagecreate(150,150);
$background = imagecolorallocate($image, 0, 0, 0); //black background
for ($row=0; $row < 15; $row++) {
for ($col=0; $col < 15; $col++) {
$x1 = 10* $col;
$y1 = 10*$row;
$x2 = 10*($col + 1);
$y2 = 10*($row + 1);
imagefilledrectangle($image, $x1,$y1,$x2,$y2, imagecolorallocate($image, 100,100,100)); //grey rectangle
}
}
imagepng($image, "ciph.png");
This works for small images no bigger than 150x150 pixels and i get a completely grey filled rectangle. but soon as i try bigger images. it only adds rectangles to part of the image. Any idea what is causing this? it appears there is a limit on the number of individual objects i can draw.
15x15
18x18
ive counted and it appears to only draw 256 rectangles... doesnt seem like a coincidence that that is 2 to the 8th power.
Any help would be much appreciated! thanks.
The problem lies in how you're creating the image. If you change first line from:
$image = imagecreate(150,150);
to:
$image = imagecreatetruecolor(150,150);
it will allow more than 256 rectangles to be drawn to the image.
imagecreatetruecolor() also gives the image a black background by default, instead of the blank background that imagecreate() gives, so you won't need the second line as well.

php imagemagick - how to square an image in the middle of a white square without cropping

I have a series of images on white backgrounds.
My problem is they are in a variety of shapes and sizes and I want them to all be equal in size and all centred in a square ratio without cropping and losing any of the actual image.
Below is my best attempt to date (using imagemagik), but the is not scaling it is just cropping square at 80x80 and losing most of the content
$im = new Imagick("myimg.jpg");
$im->trimImage(20000);
$im_props = $im->getImageGeometry();
$width = $im_props['width'];
$height = $im_props['height'];
$diff = abs($width-$height);
$color=new ImagickPixel();
$color->setColor("white");
if($width > $height){
$im->thumbnailImage(80, 0);
$im->borderImage($color, ($diff/2), 0);
}else{
$im->thumbnailImage(0, 80);
$im->borderImage($color, 0, ($diff/2));
}
$im->cropImage (80,80,0,0);
$im->writeImage("altimg.jpg");
Any help gratefully recieved
Thanks #Mark Setchel for pointing me in the right direction. I managed to achieve what I wanted, (an un-cropped image centred in a white square and trimmed to the longest side).
I have voted up your comments but thought I would post my final code for completeness.
$im = new Imagick("myimg.jpg");
$im->trimImage(20000);
$im->resizeImage(80, 80,Imagick::FILTER_LANCZOS,1, TRUE);
$im->setImageBackgroundColor("white");
$w = $im->getImageWidth();
$h = $im->getImageHeight();
$off_top=0;
$off_left=0;
if($w > $h){
$off_top = ((80-$h)/2) * -1;
}else{
$off_left = ((80-$w)/2) * -1;
}
$im->extentImage(80,80, $off_left, $off_top);
$im->writeImage("altimg.jpg");

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);

How to generate a completely random image?

I'm trying to generate a completely random image of a given size.
Here is what I have so far:
<?php
$Width = 64;
$Height = 32;
$Image = imagecreate($Width, $Height);
for($Row = 1; $Row <= $Height; $Row++) {
for($Column = 1; $Column <= $Width; $Column++) {
$Red = mt_rand(0,255);
$Green = mt_rand(0,255);
$Blue = mt_rand(0,255);
$Colour = imagecolorallocate ($Image, $Red , $Green, $Blue);
imagesetpixel($Image,$Column - 1 , $Row - 1, $Colour);
}
}
header('Content-type: image/png');
imagepng($Image);
?>
The problem is that after 4 rows it stops being random and fills with a solid colour like this
if you change imagecreate to imagecreatetruecolor it should work (everything else is the same, including the parameters)
By allocating a new color for each pixel, you are quickly exhausting the color palate. 4 rows at 64 pixels per row is 256. After the palate is full, any new color will use the last color on the palate.
Mishu's answer uses a full-color image, rather than and indexed color image, which is why you are able to allocate more colors.
See this answer in the PHP docs http://us.php.net/manual/en/function.imagecolorallocate.php#94785
Both create images with different palletes. True color has more color ranges so its better to use imagecreatetruecolor()

Image function in PHP

In javascript there is function getImageData(), is there any function in PHP similar to that.
Have a look at the GD library functions:
http://www.php.net/manual/en/ref.image.php
EDIT:
Specifically, the getimagesize function for dimensions and more :)
EDIT
Hello again. This should help. You can get the RGB values of your image like so:
// the file
$file = 'test.gif';
// get the width and height of the image
list($width, $height) = getimagesize($file);
// create your image resource
$image = imagecreatefromgif($file);
// you could use the image width and height values here to
// iterate through each pixel using two nested for loops...
// or... set specific values for $x and $y
$x = 0;
$y = 0;
// get the colour index for the current pixel
$colourIndex = imagecolorat($image, $x, $y);
// get an array of human-readable RGB values
$colourValues = imagecolorsforindex($image, $colourIndex
// display RGB values
print_r($colourValues);
GD is your best bet:
http://php.net/manual/en/function.imagecreatefromjpeg.php
You'll need to know the type (png, jpg) before you start.

Categories