Is there any way to get the x,y position of a colour in an image in PHP ?
Eg : In this image
can I get the starting point,ie the x,y positions of the colour RED.
I need to create an option for the user to change the colour of a particular portion in an image.So if the user wants to change the red colour to blue in this image.I use imagefill() function to change the colour,but it need the x,y coordinates to work.Hope this make sense.
Try something like this:
// applied only to a PNG images, You can add the other format image loading for Yourself
function changeTheColor($image, $findColor, $replaceColor) {
$img = imagecreatefrompng($image);
$x = imagesx($img);
$y = imagesy($img);
$newImg = imagecreate($x, $y);
$bgColor = imagecolorallocate($newImg, 0, 0, 0);
for($i = 0; $i < $x; $i++) {
for($j = 0; $j < $y; $j++) {
$ima = imagecolorat($img, $i, $j);
$oldColor = imagecolorsforindex($img, $ima);
if($oldColor['red'] == $findColor['red'] && $oldColor['green'] == $findColor['green'] && $oldColor['blue'] == $findColor['blue'] && $oldColor['alpha'] == $findColor['alpha'])
$ima = imagecolorallocatealpha($newImage, $replaceColor['red'], $replaceColor['green'], $replaceColor['blue'], $replaceColor['alpha']);
}
imagesetpixel($newImg, $i, $j, $ima);
}
}
return imagepng($newImg);
}
We are expecting here that $findColor and $replaceColor are arrays with this structure:
$color = array(
'red' => 0,
'green' => 0,
'blue' => 0,
'alpha' => 0,
);
Didn't try the code but it at least should point You the right way. It loops through every pixel, check the color at that pixel and if it is the one we are looking for, replaces it with the $replaceColor. If not, the very same color is then placed into the new image at the very same position.
As it uses two for loops it may be very time and memory consumpting on large images.
Related
I have some images and each of them has a masked (transparent) rectangular region inside of the same size. How can I detect the coordinates of those regions? I searched for libraries but none of them does it. I want to put my QR codes instead of the masked part.
I would try (not tested) to read the image and use a transparent color:
$src = imagecreatefrompng("your-image.png");
$trans = imageColorAllocateAlpha($src, 0, 0, 0, 127);
$transArray = imagecolorsforindex($src, $trans);
The I would read the image dimensions and check every pixel like this:
$width = imagesx($src);
$height = imagesy($src);
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$color = imagecolorat($src, $x, $y);
$colors = imagecolorsforindex($src, $color);
if ($colors["alpha"] == $transArray["alpha"]) {
// you have detected the first pixel of transparency
// here you have to remember smallest $x and smallest $y
// as well as biggest $x and biggest $y
// that should be your rectangle
}
}
}
I would like to create watermark with PHP and GD library.
I would like to do repeating the watermark logo with auto margin(space) between each repeated logo, also zigzag position.
Is it possible to create dash line watermark that connect each watermark logo?
The result would be like this:
I have finished the zigzag by using loop function and odd even clause.
/*
* utils
$widthWatermark = imagesx($logo);
$heightWatermark = imagesy($logo);
$widthPhoto = imagesx($output);
$heightPhoto = imagesy($output);
*/
// $xLogoPosition = 0;
// $yLogoPosition = 0;
$__xRepeat = ceil($widthPhoto / $widthWatermark);
$__yRepeat = ceil($heightPhoto / $heightWatermark);
$margin = (int)self::$option['margin'];
for ($i = 0; $i <= $__xRepeat; $i++) {
if ($i % 2 === 0) {
$pre_ii = 1;
} else {
$pre_ii = 0;
}
for ($ii = 0; $ii <= $__yRepeat; $ii++) {
$ii_zero = $ii - $pre_ii;
if ($ii_zero % 2 === 0) {
$y_xindent = $widthWatermark;
}else{
$y_xindent = 0;
}
$this->imagecopymerge_alpha($output, $logo, ($xLogoPosition + $widthWatermark * $i + $y_xindent), ($yLogoPosition + $widthWatermark * $ii), 0, 0, ImageSX($logo), ImageSY($logo), self::$option['opacity']);
}
}
now I stuck at how to create dashed line that have diagonal position that connect to each other logo.
I have a hint from http://php.net/manual/en/function.imagedashedline.php
but I don't how to use and combine it with my previous code that generate zigzag logo
Edit
It turns out that PHP/GD actually has a function - imagesettile() - specifically to handle this situation.
I've modified my original answer to account for this:
<?php
// create php image of a 'dashed cross'.
$crossW = $crossH = 200;
$cross = imagecreatetruecolor($crossW, $crossH);
imagefill($cross, 0, 0, 0x7fff00ff); // transparent magenta.
imagesetthickness($cross, 1);
imagesetstyle(
$cross,
array_merge(
array_fill(0, 3, 0x7fff00ff), // transparent magenta.
array_fill(0, 8, 0x60ffffff) // partially-transparent white.
)
);
imageline($cross, 0, 0, $crossW, $crossH, IMG_COLOR_STYLED);
imageline($cross, $crossW, 0, 0, $crossH, IMG_COLOR_STYLED);
$imageFile = 'wm2.jpg';
// open the image file to be watermarked and store its height and width.
$image = imagecreatefromjpeg($imageFile);
$imWidth = imagesx($image);
$imHeight = imagesy($image);
// apply the cross pattern as a tile to the image file.
imagesettile($image, $cross);
imagefilledrectangle($image, 0, 0, $imWidth, $imHeight, IMG_COLOR_TILED);
header('Content-type: image/png');
imagepng($image);
imagedestroy($cross);
imagedestroy($image);
exit;
Input:
Result:
The following block of php code is supposed to take this image as input snd produce this image as output (convert the black to yellow and the light-blue to black):
However, I'm getting this image as output instead.
Can anyone see the problem with my code?
$im = imagecreatefrompng("./input.png");
$width = imagesx($im);
$height = imagesy($im);
$new = imagecreate($width, $height);
imagecopy($new, $im, 0, 0, 0, 0, $width, $height);
imagecolorset($new, imagecolorexact($new, 0, 0, 0), 255, 255, 0);
for($i = 0; $i < $width; $i++) {
for($j = 0; $j < $height; $j++) {
$index = imagecolorat($new, $i, $j);
$rgb = imagecolorsforindex($new, $index);
if($rgb['red'] != 255 && $rgb['green'] != 255 && $rgb['blue'] != 0) {
echo '(' . $i . ', ' . $j . ')' . 'color => (' . (255 - $rgb['blue']) . ', ' . (255 - $rgb['blue']) . ', 0)<br />';
$color = imagecolorallocate($new, 255 - $rgb['blue'], 255 - $rgb['blue'], 0);
imagesetpixel($new, $i, $j, $color);
}
unset($index);
unset($rgb);
}
}
imagepng($new, 'tesst.png');
imagedestroy($im);
imagedestroy($new);
I believe the source of the issue here is that when using a palette based image, such as the one you have created by calling imagecreate(), it is possible to declare the same color at multiple indexes within the palette.
This means that, because you are calling imagecolorallocate() on every iteration, the palette eventually becomes full and imagecolorallocate() starts returning false (this can be seen if you var_dump($color); before the imagesetpixel() call). false evaluates to zero when cast to an integer - so when the palette fills up, all remaining pixels are effectively converted to the background color.
There are two things that you could do about this, the first and probably easiest is just to use a true-color image, which is just a simple case of changing imagecreate($width, $height); to imagecreatetruecolor($width, $height);.
If you want to stick with the palette-based image (for example for reasons of output image data size - with an image containing so few colors, a palette-based image will be considerably smaller), you will need to cache the allocated colors manually so you can re-use them, something like this:
// ...
$colors = array();
for ($x = 0; $x < $width; $x++) { // iterate x axis
for ($y = 0; $y < $height; $y++) { // iterate y axis
// Get the color at this index
$index = imagecolorat($new, $x, $y);
// Only allocate a new color if not already done
if (!isset($colors[$index])) {
$rgb = imagecolorsforindex($new, $index);
if ($rgb['red'] != 255 || $rgb['green'] != 255 || $rgb['blue'] != 0) {
// If it's not the background color allocate a new color
$r = $g = 255 - $rgb['blue'];
$b = 0;
$colors[$index] = imagecolorallocate($new, $r, $g, $b);
} else {
// Otherwise set the index to false, we can ignore it
$colors[$index] = false;
}
}
// If there's something to do, do it
if ($colors[$index] !== false) {
imagesetpixel($new, $x, $y, $colors[$index]);
}
}
}
// ...
You may also wish to track the colors in use in the image so you can "cleanse the palette" afterwards (i.e. deallocate any colors that are no longer in use in the image, which will further help with the data size reduction). Although arguably it would be better, in this case, to start with a clean palette and inspect the old image resource to get the pixel details, instead of copying the original to a new image resource.
yes your
$color = imagecolorallocate($new, 255 - $rgb['blue'], 255 - $rgb['blue'], 0);
is messing everything up..
if you desire the same output... just paste the line outside for loop this will solve your problem if you want the specific image:
$color = imagecolorallocate($new, 35, 35, 0); //got from debugging
and it will get u the desired output.
Dins
I have the following PHP method that generates new users a profile image when they first sign up, before they upload their own. All it does is just create them a brightly colored square - something to make the interface look a little more interesting when showing a list of users without profile photos.
How could I adapt this method so it created a checkerboard of random colors? Something like this: http://krazydad.com/bestiary/thumbs/random_pixels.jpg
public function generate_random_image($filename, $w = 200, $h = 200, $chosen_color = NULL) {
if(!$chosen_color) {
$color_options = Array("#6f0247", "#FF0569", "#FFF478", "#BAFFC0", "#27DB2D", "#380470", "#9D69D6");
$random = rand(0,sizeof($color_options));
$chosen_color = $color_options[$random];
}
$rgb = self::hex2rgb($chosen_color);
$image = imagecreatetruecolor($w, $h);
for($row = 1; $row <= $h; $row++) {
for($column = 1; $column <= $w; $column++) {
$color = imagecolorallocate ($image, $rgb[0] , $rgb[1], $rgb[2]);
imagesetpixel($image,$column - 1 , $row - 1, $color);
}
$row_count++;
}
$filename = APP_PATH.$filename;
imagepng($image, $filename);
return $chosen_color;
}
How about you just change
$color = imagecolorallocate ($image, $rgb[0] , $rgb[1], $rgb[2]);
to
$color = imagecolorallocate ($image, rand(0,255), rand(0,255), rand(0,255));
Then, each pixel will have it's own colour. Just draw to a small image, then scale by 200% or 300% (or some other arbitrary number) and you'll get nice, big chunky pixels like the image you linked.
While iterating through $rows and $columns you should increase step to desired pixel size and choose another color with each iteration.
example for pixel width = 20x20 :
$pixel = 20;
for($row = 0; $row <= $h / $pixel; $row++) {
for($column = 0; $column <= $w/ $pixel; $column++) {
$rgb = self::hex2rgb($color_options[rand(0,sizeof($color_options))]);
$color = imagecolorallocate ($image, $rgb[0] , $rgb[1], $rgb[2]);
imagefilledrectangle(
$image,
$column*$pixel,
$row*pixel,
$column*$pixel+$pixel,
$row*pixel+$pixel,
$color
);
}
}
I'm creating a graphical menu dynamically from PHP, it results in only 1 image that looks like this:
One Two Three Four
The problem however is, that I have to determine the x-offset and the width of each page title (e.g. the left offset and the width of "One" in pixels) to position the image with css.
All works fine, except for page titles that contain spaces - imagettfbbox() returns the wrong positions. I am using an arial font (TTF)
Any suggestions on how I get around this issue?
Edit: I got it working now, using the following function to determine the text's bounds:
function calculateTextBox($font_size, $font_angle, $font_file, $text) {
$box = imagettfbbox($font_size, $font_angle, $font_file, $text);
$min_x = min(array($box[0], $box[2], $box[4], $box[6]));
$max_x = max(array($box[0], $box[2], $box[4], $box[6]));
$min_y = min(array($box[1], $box[3], $box[5], $box[7]));
$max_y = max(array($box[1], $box[3], $box[5], $box[7]));
return array(
'left' => ($min_x >= -1) ? -abs($min_x + 1) : abs($min_x + 2),
'top' => abs($min_y),
'width' => $max_x - $min_x,
'height' => $max_y - $min_y,
'box' => $box
);
}
Edit 2: Unfortunately I keep getting wrong dimensions when using different font sizes and font files...
One way to do it would be to use the imagecolorat function to work out the colour of a pixel at particular point in the image, its not going to be the quickest or even necessarily the best way to do it but it should work.
Something like this (searching for black pixels) should work, and will return the bounds you're looking for which you can then translate into x/ co-ordinates if you want:
function get_bounds($image)
{
$height = imagesy($image);
$width = imagesx($image);
$to_return = new stdClass();
$to_return->top = null;
$to_return->bottom = null;
$to_return->left = null;
$to_return->right = null;
for ($x = 0; $x < $width; $x++)
{
for ($y = 0; $y < $height; $y++)
{
$color = imagecolorat($image, $x, $y);
$rgb = imagecolorsforindex($image, $color);
// If its black
if (($rgb['red'] == 0) && ($rgb['green'] == 0) && ($rgb['blue'] == 0))
{
if (($y < $to_return->top) || is_null($to_return->top))
{
$to_return->top = $y;
}
if (($y > $to_return->bottom) || is_null($to_return->bottom))
{
$to_return->bottom = $y;
}
if (($x < $to_return->left) || is_null($to_return->left))
{
$to_return->left = $x;
}
if (($x > $to_return->right) || is_null($to_return->right))
{
$to_return->right = $x;
}
}
}
}
return $to_return;
}
Actually, its real bounds can be calculated from imagettftext function's return values.