Drawing a grid with coordinates in each cell onto existing image - php

I'm trying to make a function that takes a JPG file, add a grid overlay, and in each cell write in text A1, A2, A3 and so on.
The current code (below) only draws the grid, with a static column/row size.
Question 1) How can I add the coordinates as text in each cell? E.g rows are letters, and columns are numbers. So first row is A1, A2, A3 ... and next row is B1, B2, B3.
Question 2) How can I modify it, so that I specify just how many rows and columns I want, and it will automatically adjust the size of the col/rows accordingly to fit the dimensions of the input image?
function draw_grid(&$img, $x0, $y0, $width, $height, $cols, $rows, $color) {
imagesetthickness($img, 5);
//draw outer border
imagerectangle($img, $x0, $y0, $x0+$width*$cols, $y0+$height*$rows, $color);
//first draw horizontal
$x1 = $x0;
$x2 = $x0 + $cols*$width;
for ($n=0; $n<ceil($rows/2); $n++) {
$y1 = $y0 + 2*$n*$height;
$y2 = $y0 + (2*$n+1)*$height;
imagerectangle($img, $x1,$y1,$x2,$y2, $color);
}
//then draw vertical
$y1 = $y0;
$y2 = $y0 + $rows*$height;
for ($n=0; $n<ceil($cols/2); $n++) {
$x1 = $x0 + 2*$n*$width;
$x2 = $x0 + (2*$n+1)*$width;
imagerectangle($img, $x1,$y1,$x2,$y2, $color);
}
}
$imgpath = "foto/306/306.jpg";
$img = imagecreatefromjpeg($imgpath);
$size = getimagesize($imgpath);
$width = $size[0];
$height = $size[1];
$red = imagecolorallocate($img, 255, 0, 0);
draw_grid($img, 0,0, $width /10 , $height /10 ,20,10,$red);
header("Content-type: image/jpg");
imagejpeg($img);
imagedestroy($img);

As described in my comment, your current code is only drawing the outlines. This is fine for drawing a Grid, but if you wish to add some text to the cell, you have to draw each rectangle manual, and use those coordinates to place the text.
Using imagettfbbox, you can calculate the width/height of the text, you'll need that information to 'center' the text in to the cell.
Regarding your second question, dividing the total picture width with the number of cells you want so you'll know the size of each individual cell.
I've updated your code to show the general idea of calculation the x/y coordinates
<?php
$imgpath = "duck.jpg";
$img = imagecreatefromjpeg($imgpath);
$size = getimagesize($imgpath);
$width = $size[0];
$height = $size[1];
$red = imagecolorallocate($img, 255, 0, 0);
// Number of cells
$xgrid = 5;
$ygrid = 5;
// Calulate each cell width/height
$xgridsize = $width / $xgrid;
$hgridsize = $height / $ygrid;
// Remember col
$c = 'A';
// Y
for ($j=0; $j < $ygrid; $j++) {
// X
for ($i=0; $i < $xgrid; $i++) {
// Dynamic x/y coords
$sy = $hgridsize * $j;
$sx = $xgridsize * $i;
// Draw rectangle
imagerectangle($img, $sx, $sy, $sx + $xgridsize, $sy + $hgridsize, $red);
// Draw text
addTextToCell($img, $sx, $xgridsize, $sy + $hgridsize, $hgridsize, $c . ($i + 1));
}
// Bumb cols
$c++;
}
function addTextToCell($img, $cellX, $cellWidth, $cellY, $cellHeight, $text) {
// Calculate text size
$text_box = imagettfbbox(20, 0, 'OpenSans', $text);
$text_width = $text_box[2]-$text_box[0];
$text_height = $text_box[7]-$text_box[1];
// Calculate x/y position
$textx = $cellX + ($cellWidth / 2) - $text_width;
$texty = $cellY - ($cellHeight / 2) - $text_height;
// Set color and draw
$color = imagecolorallocate($img, 0, 0, 255);
imagettftext($img, 20, 0, $textx, $texty, $color, 'OpenSans', $text);
}
// Save output as file
imagejpeg($img, 'output.jpg');
imagedestroy($img);
shell_exec('open -a Preview output.jpg');

1) Check out the imagettftext() and imagefttext() functions. One of those should do what you want.
2) Divide the width and height of the input image by the number of columns and rows respectively that you want to divide it into to get the width and height of each cell.

Related

How to detect masked region on an image in PHP?

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
}
}
}

Position array text in PHP GD on middle of specific part of image

I have an array of a string to fit on a page, i managed to center it horizontal now i need to do that vertical in a specific part of image.
$imgWidth = 240;
$imgHeight = 900;
$IMG = imagecreatetruecolor($imgWidth,$imgHeight);
$font_type_bold = '/dejavu/DejaVuSansCondensed-Bold.ttf';
$background = imagecolorallocate($IMG, 78,129,154);
$text_white = imagecolorallocate($IMG, 255,255,255);
$IMG = imagecreatetruecolor($imgWidth,$imgHeight);
$max_lenght_of_title = 15;
$special_issue_name_mod="This is some example text to be put on the page and split into array to fit he width of frame";
$text = explode("\n", wordwrap($special_issue_name_mod, $max_lenght_of_title));
$line_height=30;
imageline($IMG, 0, 500, 240, 500, $text_white);
imageline($IMG, 0, 100, 240, 100, $text_white);
for($i=0;$i<count($text);$i++) {
$font_size_si_name_horizontal = 21;
//Center the text
$size = imagettfbbox(20, 0, $font_type_bold, $text[$i]);
$long_text = $size[2] + $size[0];
$posx = ($imgWidth - $long_text) / 2;
imagettftext($IMG, $font_size_si_name_horizontal, 0, $posx - 5, 150+ $line_height + $line_height * $i , $text_white, $font_type_bold, $text[$i]);
}
header("Content-type: image/png");
imagepng($IMG);
imagecolordeallocate($IMG, $text_color );
imagecolordeallocate($IMG, $background );
The result is this and i need it to be in specific part of page, for example selected one
So how can i make this to not be the fixed middle but adjustive one depending on what part of page the text will be.
Note: Text can be longer, so thats the main problem. Depending on lenght of text the title should be in the middle
<?php
$imgWidth = 240;
$imgHeight = 900;
$IMG = imagecreatetruecolor($imgWidth,$imgHeight);
$font_type_bold = 'BioRhyme/BioRhyme-Regular.ttf';
$background = imagecolorallocate($IMG, 78,129,154);
$text_white = imagecolorallocate($IMG, 255,255,255);
$IMG = imagecreatetruecolor($imgWidth,$imgHeight);
$max_lenght_of_title = 15;
$special_issue_name_mod="This is some example text to be put on the page and split into array to fit he width of frame";
$text = explode("\n", wordwrap($special_issue_name_mod, $max_lenght_of_title));
$line_height=30;
$vert = ($imgHeight/5);
for($i=0;$i<count($text);$i++) {
$font_size_si_name_horizontal = 21;
//Center the text
$size = imagettfbbox(20, 0, $font_type_bold, $text[$i]);
$long_text = $size[2] + $size[0];
$posx = ($imgWidth - $long_text) / 2;
imagettftext($IMG, $font_size_si_name_horizontal, 0, $posx - 5, $vert+ $line_height + $line_height * $i , $text_white, $font_type_bold, $text[$i]);
}
header("Content-type: image/png");
imagepng($IMG);
imagecolordeallocate($IMG, $text_color );
imagecolordeallocate($IMG, $background );
?>
I have added $vert = ($imgHeight/5); and put into imagettftext() for dynamically calculted vertical Y index!
I figured out a way to solve this
$middle_of_page = 350;
for($i=0;$i<count($text);$i++) {
$middle_of_page = $middle_of_page - 21;
}
imagettftext($IMG, $font_size_si_name_horizontal, 0, $posx - 5, $middle_of_page+$line_height + $line_height * $i , $text_white, $font_type_bold, $text[$i]);
With the $middle_of_page part i regulate the point where text with array size of one will be, and then i just remove by font size of 21 for every $i that stands for array size of string.

Drawing on image with the GD library in PHP

I have created a code to generate a random pattern image. it creates an image with given width and height and fills it with small 40x40 pixel rectangles.
this is my code:
<?php
$width = 1000;
$height = 600;
$image_p = imagecreate($width, $height);
$baseR = 255 - rand(0, 100);
$baseG = 255 - rand(0, 100);
$baseB = 255 - rand(0, 100);
for ($i = 0; $i <= floor($width / 40); $i++){
for ($j = 0; $j <= floor($height / 40); $j++){
$val = floor(100 * (rand(0, 100) / 100));
$r = $baseR - $val;
$g = $baseG - $val;
$b = $baseB - $val;
$color = imagecolorallocate($image_p, $r, $g, $b);
imagefilledrectangle($image_p, $i * 40, $j * 40, (($i + 1) * 40), (($j + 1) * 40), $color);
}
}
imagejpeg($image_p, 'my_dir/test.jpg');
?>
there's no problem when i set the width to a value like 640 and the height to 400. but if i set the width to 1000 and the height to 800, there will be a blank area on the right side of the image which is not covered by rectangles.
I implemented the same code in delphi and it worked perfectly, but in PHP...!
Change imagecreate to imagecreatetruecolor
You're creating a palette based image with 255 colors max. You're running out of colors to allocate at the end and it's recycling the last color on the palette for the remainder of the blocks.

Multiply filter with PHP's GD library

I've tried experimenting with the GD library to simulate Photoshop's muliply effect, but I haven't found a working solution yet.
According to Wikipedia, the multiply blend mode:
[...] multiplies the numbers for each pixel of the top layer with the corresponding pixel for the bottom layer. The result is a darker picture.
Does anyone know of a way to achieve this using PHP? Any help would be much appreciated.
You need to take every pixel of your image, then multiply each RGB value with your background color / 255 (it's the Photoshop formula). For example, a JPG file with a red background color multiply filter, saved as a PNG file for better results:
<?php
$filter_r=216;
$filter_g=0;
$filter_b=26;
$suffixe="_red";
$path=YOURPATHFILE;
if(is_file($path)){
$image=#imagecreatefromjpeg($path);
$new_path=substr($path,0,strlen($path)-4).$suffixe.".png";
$imagex = imagesx($image);
$imagey = imagesy($image);
for ($x = 0; $x <$imagex; ++$x) {
for ($y = 0; $y <$imagey; ++$y) {
$rgb = imagecolorat($image, $x, $y);
$TabColors=imagecolorsforindex ( $image , $rgb );
$color_r=floor($TabColors['red']*$filter_r/255);
$color_g=floor($TabColors['green']*$filter_g/255);
$color_b=floor($TabColors['blue']*$filter_b/255);
$newcol = imagecolorallocate($image, $color_r,$color_g,$color_b);
imagesetpixel($image, $x, $y, $newcol);
}
}
imagepng($image,$new_path);
}
?>
I've been looking for Multiply blend between two images as well and couldn't find any native-php solution for it. It appears that only way (for now) is to "manually" set pixels, pixel-by-pixel. Here's my code that does Multiply blend between two images, assuming that images are of the same size. You can adjust it to handle different sizes if you like.
function multiplyImage($dst,$src)
{
$ow = imagesx($dst);
$oh = imagesy($dst);
$inv255 = 1.0/255.0;
$c = imagecreatetruecolor($ow,$oh);
for ($x = 0; $x <$ow; ++$x)
{
for ($y = 0; $y <$oh; ++$y)
{
$rgb_src = imagecolorsforindex($src,imagecolorat($src, $x, $y));
$rgb_dst = imagecolorsforindex($dst,imagecolorat($dst, $x, $y));
$r = $rgb_src['red'] * $rgb_dst['red']*$inv255;
$g = $rgb_src['green'] * $rgb_dst['green']*$inv255;
$b = $rgb_src['blue'] * $rgb_dst['blue']*$inv255;
$rgb = imagecolorallocate($c,$r,$g,$b);
imagesetpixel($c, $x, $y, $rgb);
}
}
return $c;
}
Function returns image object so you should ensure to do imagedestroy after you're done using it.
There should be a workaround using overlay native-php blend, which suggests that 50% gray pixels of destination image will be affected by source pixels. In theory, if you do need to blend two black-and-white images (no gray tones), if you set contrast of destination image so white will become 50%-gray, and then overlay-blend source image over it, should give you something similar to multiply. But for color images, or grayscale images, this wouldn't work - above method appears to be the only option.
I was led into this thread when I needed to blend two images in GD. It seems there is no code specifically for that so I will just leave this here for future visitors to this page.
This is a fork from the answer of colivier that supports multiply-blending of two images.
The two images need not be of the same size BUT the overlaying image will be resized and cropped to the size of the bottom layer. I made a fit helper function to do just that but don't bother with that.
imagecolorat returns the base color, even with PNGs with transparency. That is, a 50% black (visible as (128, 128, 128)) will be returned as (0, 0, 0, 64) 64 being the alpha value. This code takes into consideration translucency and converts the translucent colors to the visible color values.
// bottom layer
$img1 = imagecreatefromjpeg(realpath(__DIR__.'/profilePic.jpg'));
// top layer
$img2 = imagecreatefrompng(realpath(__DIR__.'/border2.png'));
imagealphablending($img2, false);
imagesavealpha($img2, true);
$imagex = imagesx($img1);
$imagey = imagesy($img1);
$imagex2 = imagesx($img2);
$imagey2 = imagesy($img2);
// Prereq: Resize img2 to match img1, cropping beyond the aspect ratio
$w1 = max(min($imagex2, $imagex), $imagex);
$h1 = max(min($imagey2, $imagey), $imagey);
$w_using_h1 = round($h1 * $imagex2 / $imagey2);
$h_using_w1 = round($w1 * $imagey2 / $imagex2);
if ($w_using_h1 > $imagex) {
fit($img2, $imagex, $imagey, 'HEIGHT', true);
}
fit($img2, $imagex, $imagey, 'WIDTH', true);
// Actual multiply filter
for ($x = 0; $x < $imagex; ++$x) {
for ($y = 0; $y < $imagey; ++$y) {
$rgb1 = imagecolorat($img1, $x, $y);
$rgb2 = imagecolorat($img2, $x, $y);
$idx1 = imagecolorsforindex($img1, $rgb1);
$idx2 = imagecolorsforindex($img2, $rgb2);
// Shift left 8, then shift right 7
// same as multiply by 256 then divide by 128
// approximate multiply by 255 then divide by 127
// This is basically multiply by 2 but, expanded to show that
// we are adding a fraction of white to the translucent image
// $adder = ($idx2['alpha'] << 8 >> 7);
$adder = ($idx2['alpha'] << 1);
$rmul = min(255, $idx2['red'] + $adder);
$gmul = min(255, $idx2['green'] + $adder);
$bmul = min(255, $idx2['blue'] + $adder);
$color_r = floor($idx1['red'] * $rmul / 255);
$color_g = floor($idx1['green'] * $gmul / 255);
$color_b = floor($idx1['blue'] * $bmul / 255);
$newcol = imagecolorallocatealpha($img1, $color_r, $color_g, $color_b, 0);
imagesetpixel($img1, $x, $y, $newcol);
}
}
imagejpeg($img1, __DIR__.'/out.jpg');
/**
* Fits an image to a $w x $h canvas
*
* #param type $w Target width
* #param type $h Target height
* #param int $fit_which Which dimension to fit
* #param bool $upscale If set to true, will scale a smaller image to fit the given dimensions
* #param bool $padded If set to true, will add padding to achieve given dimensions
*
* #return Image object
*/
function fit(&$img, $w, $h, $fit_which = 'BOTH', $upscale = false, $padded = true) {
if (!in_array($fit_which, array('WIDTH', 'HEIGHT', 'BOTH'))) {
$fit_which = 'BOTH';
}
$w0 = imagesx($img);
$h0 = imagesy($img);
if (!$upscale && $w0 <= $w && $h0 <= $h)
return $this;
if ($padded) {
$w1 = max(min($w0, $w), $w);
$h1 = max(min($h0, $h), $h);
}
else {
$w1 = min($w0, $w);
$h1 = min($h0, $h);
}
$w_using_h1 = round($h1 * $w0 / $h0);
$h_using_w1 = round($w1 * $h0 / $w0);
// Assume width, crop height
if ($fit_which == 'WIDTH') {
$w2 = $w1;
$h2 = $h_using_w1;
}
// Assume height, crop width
elseif ($fit_which == 'HEIGHT') {
$w2 = $w_using_h1;
$h2 = $h1;
}
elseif ($fit_which == 'BOTH') {
if (!$padded) {
$w2 = $w = min($w, $w_using_h1);
$h2 = $h = min($h, $h_using_w1);
}
else {
// Extend vertically
if ($h_using_w1 <= $h) {
$w2 = $w1;
$h2 = $h_using_w1;
}
// Extend horizontally
else {
$w2 = $w_using_h1;
$h2 = $h1;
}
}
}
$im2 = imagecreatetruecolor($w, $h);
imagealphablending($im2, true);
imagesavealpha($im2, true);
$transparent = imagecolorallocatealpha($im2, 255, 255, 255, 127);
imagefill($im2, 0, 0, $transparent);
imagealphablending($img, true);
imagesavealpha($img, true);
// imagefill($im, 0, 0, $transparent);
imagecopyresampled($im2, $img, ($w - $w2) / 2, ($h - $h2) / 2, 0, 0, $w2, $h2, $w0, $h0);
$img = $im2;
}
Have you tried to use php manual?
For people looking to apply a 'multiply' effect on images like the one in Photoshop (generally b&w ones), you can achieve it with the IMG_FILTER_COLORIZE filter.
<?php
function multiplyColor(&$im, $color = array(255, 0, 0)) {
//get opposite color
$opposite = array(255 - $color[0], 255 - $color[1], 255 - $color[2]);
//now we subtract the opposite color from the image
imagefilter($im, IMG_FILTER_COLORIZE, -$opposite[0], -$opposite[1], -$opposite[2]);
}
?>
If used with png image and alpha must be well and works very well
$filter_r=215;
$filter_g=5;
$filter_b=5;
$alpha=70;
$suffixe="_red";
$path="./img/foto_220_590.png";
if(is_file($path)){
$image=imagecreatefrompng($path);
$new_path=substr($path,0,strlen($path)-4).$suffixe.".png";
echo $imagex = imagesx($image);
echo $imagey = imagesy($image);
for ($x = 0; $x <$imagex; ++$x) {
for ($y = 0; $y <$imagey; ++$y) {
$rgb = imagecolorat($image, $x, $y);
$TabColors=imagecolorsforindex ( $image , $rgb );
$color_r=floor($TabColors['red']*$filter_r/255);
$color_g=floor($TabColors['green']*$filter_g/255);
$color_b=floor($TabColors['blue']*$filter_b/255);
//$newcol = imagecolorallocate($image, $color_r,$color_g,$color_b);
// this new alpha
$newcol = imagecolorallocatealpha($image, $color_r,$color_g,$color_b,$alpha);
imagesetpixel($image, $x, $y, $newcol);
}
}
imagepng($image,$new_path);
I updated #colivier script to be able to myltiply two images, and not just an image with a color:
/**
* Multiply $pathToDst and $pathToSrc to $resultPath
*
* #param string $pathToDst
* #param string $pathToSrc
* #param string $resultPath
*/
function multiply($pathToDst, $pathToSrc, $resultPath) {
switch (pathinfo($pathToDst, PATHINFO_EXTENSION)) {
case "gif" :
$resourceDst = imagecreatefromgif($pathToDst);
break;
case "png" :
$resourceDst = imagecreatefrompng($pathToDst);
break;
default :
$resourceDst = imagecreatefromjpeg($pathToDst);
break;
}
switch (pathinfo($pathToSrc, PATHINFO_EXTENSION)) {
case "gif" :
$resourceSrc = imagecreatefromgif($pathToSrc);
break;
case "png" :
$resourceSrc = imagecreatefrompng($pathToSrc);
break;
default :
$resourceSrc = imagecreatefromjpeg($pathToSrc);
break;
}
for ($x = 0; $x < 400; ++$x) {
for ($y = 0; $y < 400; ++$y) {
$TabColorsFlag = imagecolorsforindex($resourceDst, imagecolorat($resourceDst, $x, $y));
$TabColorsPerso = imagecolorsforindex($resourceSrc, imagecolorat($resourceSrc, $x, $y));
$color_r = floor($TabColorsFlag['red'] * $TabColorsPerso['red'] / 255);
$color_g = floor($TabColorsFlag['green'] * $TabColorsPerso['green'] / 255);
$color_b = floor($TabColorsFlag['blue'] * $TabColorsPerso['blue'] / 255);
imagesetpixel($resourceDst, $x, $y, imagecolorallocate($resourceSrc, $color_r, $color_g, $color_b));
}
}
imagepng($resourceDst, $resultPath, 0);
imagedestroy($resourceDst);
imagedestroy($resourceSrc);
}

Generate random checkerboard image with a variety of colors in PHP

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

Categories