Extracting certain parts on image with PHP - php

I would like to be able to extract radar data from gif which occurs every hour.
This is an example of one gif:
Data colors can vary according data legend on the right. I would also like that country borders are not visible, as gif is drown in order: base layer, radar data layer, borders layer - I only need pure radar data.
Any suggestion or solution will be very much appriciated!

All the radar data seems to be highly saturated, so you could extract the saturation onto its own layer and threshold it at, say, 85% and use that as the opacity like this:
convert radar.gif \
\( +clone -colorspace hsl -channel S -separate -threshold 85% \) \
-compose copyopacity -composite result.gif
which gives you this, where all the unsaturated colours are now transparent:
As you can see, it is correctly picking up and retaining all the colours in the "key" on the right, but it also picks up the mauve on the left of the image. Let's make that specific tone transparent:
convert radar.gif \
\( +clone -colorspace hsl -channel S -separate -threshold 85% \) \
-compose copyopacity -composite \
-fuzz 10% -fill none -opaque "rgb(160,153,255)" result.gif
which gives this:
Now, I presume we need to get rid of the sickly yellow too:
convert radar.gif \
\( +clone -colorspace hsl -channel S -separate -threshold 85% \) \
-compose copyopacity -composite \
-fuzz 10% -fill none \
-opaque "rgb(160,153,255)" \
-opaque "rgb(254,251,175)" result.gif
Finally, let's crop to the top left 480x480 px:
convert radar.gif -crop 480x480+0+0 +repage \
\( +clone -colorspace hsl -channel S -separate -threshold 85% \) \
-compose copyopacity -composite \
-fuzz 10% -fill none \
-opaque "rgb(160,153,255)" \
-opaque "rgb(254,251,175)" result.gif

You can use 'imagecreatefromgif' to retrieve the gif and load it as an GD object (requires GD enabled in the php.ini). From there you can use quite a few image manipulation tricks to cut down to what you want from the gif. The PHP.net site has an example on what you can do with it.

First of all, if you are interested just in the information coded by colours, which us present in some specific frame of the gif, read here about how you can access only one frame if that gif:
Accessing gif frames
After that, considering you have a rectangle image, you can scan pixel by pixel, as a matrix, using php function 'imagecolorat'.

Related

imagemagick trim the bottom from transparent PNG

I am attempting to write an imagemagick command to trim the transparent pixels from the bottom of a transparent PNG. I found these commands and modified them to take off just the bottom. However the output is not as expected.
With some trial and error I've identified that the command appears to be generating an image of a minimum height. If the design is too high it leaves transparent pixels underneath. But nothing jumps out at me as being the element of the command that causes that?
I've included my 3 files that I trimmed and the 3 results that the following command(s)
generates:
convert \( ORIGINAL.png -bordercolor none -border 1x0 \) -size 1x1 xc:black -gravity west -composite -size 1x1 xc:black -gravity east -composite -size 1x1 xc:black -gravity north -composite -fuzz 10% -trim +repage -bordercolor white -shave 1x0 TRIMMED.png
convert \( ORIGINAL2.png -bordercolor none -border 1x0 \) -size 1x1 xc:black -gravity west -composite -size 1x1 xc:black -gravity east -composite -size 1x1 xc:black -gravity north -composite -fuzz 10% -trim +repage -bordercolor white -shave 1x0 TRIMMED2.png
convert \( ORIGINAL3.png -bordercolor none -border 1x0 \) -size 1x1 xc:black -gravity west -composite -size 1x1 xc:black -gravity east -composite -size 1x1 xc:black -gravity north -composite -fuzz 10% -trim +repage -bordercolor white -shave 1x0 TRIMMED3.png
If someone could please explain what I am missing in terms of this height issue that would be really appreciated.
These are the 3 ORIGINAL files and the 3 outcomes showing how the outcome changes with the original file changes.
This is what I am trying to acheive - delete the empty space from the bottom of the image. I want no space after the image at the moment if it is too high I still get space underneath (see the last outcome, bottom right)
---- BELOW ARE JUST THE ORIGINAL FILES IF ANYONE WANTED TO TRY IT ON THEIR SETUP ETC ----
If you're trying to remove what a "-trim" would remove, but only from the bottom edge of an image, this command should give you that result...
convert input.png -background none -set page %[#] \
-set option:distort:viewport %[w]x%[fx:page.y+page.height] \
+repage -distort SRT 0 result.png
It starts by setting some variables that contain the results of a "-trim" operation, but without actually removing anything. Then it uses those variables to calculate the after-trim dimensions for the output viewport. Then it uses a no-op "-distort" to effectively crop the image to the calculated output dimensions, removing only the excess transparent pixels toward the bottom.

How to fill the closed regions by a border color when outlining a transparent image using Imagick PHP

I'd like to outline an object in a transparent background with a 20px border. But, I want to fill in the closed regions with the border color.
$image = new Imagick('./img/hinata.png');
$mask = clone $image;
$mask->separateImageChannel(Imagick::CHANNEL_ALPHA);
$mask->negateImage(true);
$mask->edgeImage(20);
$mask->opaquePaintImage("white","blue",65000,false);
//// TODO: I don't know how to fill the holes
$mask->transparentPaintImage("black",0.0,0,false);
$image->compositeImage($mask,Imagick::COMPOSITE_DEFAULT,0,0);
I referenced this question:
Outline a transparent image using imagick PHP
This is the image:
This is what I want to achieve:
This is not what I want to achieve:
This is how I would do it in ImageMagick command line.
Make the background under the transparency blue.
Extract the alpha channel.
Dilate the alpha channel.
Use connected components to fill in any "holes" smaller than some threshold in area.
Replace the old alpha channel with the new one
Input: from here
convert cartoon_girl.png -background blue -alpha background \
\( -clone 0 -alpha extract \
-morphology dilate diamond:12 \
-define connected-components:mean-color=true \
-define connected-components:area-threshold=500 \
-connected-components 8 \) \
-alpha off -compose copy_opacity -composite \
result.png
Unfortunately, Imagick does not support connected components as far as I know. So the only other way would be to use flood fill at some point inside each "hole". That means you have to pick x,y coordinate inside each hole to use for doing the flood fill after doing the dilate. See https://www.php.net/manual/en/imagick.floodfillpaintimage.php
convert cartoon_girl.png -background blue -alpha background \
\( -clone 0 -alpha extract \
-morphology dilate diamond:12 \
-fuzz 80% -fill white \
-draw "color 100,310 floodfill" \
-draw "color 200,235 floodfill" -alpha off \) \
-alpha off -compose copy_opacity -composite \
result2.png

Optimising concurrent ImageMagick Requests using redis/php-resque

I am working on a site that uses ImageMagick to generate images. The site will get hundreds of request every minute and using ImageMagick to do this causes the site to crash.
So we implemented Redis and Php-resque to do the ImageMagick generating in the background on a seperate server so that it doesn't crash our main one. The problem is that it's still taking a very long time to get images done. A user might expect to wait up to 2-3 minutes for an image request because the server is so busy processing these images.
I am not sure what information to give you, but I'm more looking for advice. I think if we can cut down the initial process time for the ImageMagick request, then obviously this will help speed up the amount of images we can process.
Below is a sample of the ImageMagick script that we use:
convert -size 600x400 xc:none \( ".$path."assets/images/bases/base_image_69509021433289153_8_0.png -fill rgb\(255,15,127\) -colorize 100% \) -composite \( ".$path."assets/images/bases/eye_image_60444011438514404_8_0.png -fill rgb\(15,107,255\) -colorize 100% \) -composite \( ".$path."assets/images/markings/marking_clan_8_marking_10_1433289499.png -fill rgb\(255,79,79\) -colorize 100% \) -composite \( ".$path."assets/images/bases/shading_image_893252771433289153_8_0.png -fill rgb\(135,159,255\) -colorize 100% \) -compose Multiply -composite \( ".$path."assets/images/highlight_image_629750231433289153_8_0.png -fill rgb\(27,35,36\) -colorize 100% \) -compose Overlay -composite \( ".$path."assets/images/lineart_image_433715161433289153_8_0.png -fill rgb\(0,0,0\) -colorize 100% \) -compose Over -composite ".$path."assets/generated/queue/tempt_preview_27992_userid_0_".$filename."_file.png
My theory is that the reason this takes quite a long time is due to the process of colouring the images. Is there a way to optimise this process at all?
Anyone who has some experience with handling heavy loads of imagemagick processes or can see some glaringly easy ways to optimise our requests, I'd be very greatful.
Thank you :)
Your command actually boils down to this:
convert -size 600x400 xc:none \
\( 1.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 2.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 3.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 4.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 5.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 6.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
result.png
My thoughts are as follows:
Point 1:
The first -composite onto a blank canvas seems pointless - presumably 1.png is a 600x400 PNG with transparency, so your first line can avoid the compositing operation and save 16% of the processing time by changing to:
convert -background none 1.png -fill ... -colorize 100% \
\( 2.png ..
\( 3.png ...
Point 2
I put the equivalent of your command into a loop and did 100 iterations and it takes 15 seconds. I then changed all your reads of PNG files into reads of MPC files - or Magick Pixel Cache files. That reduced the processing time to just under 10 seconds, i.e. by 33%. A Magic Pixel Cache is just a pre-decompressed, pre-decoded file that can be read directly into memory without any CPU effort. You could pre-create them whenever your catalogue changes and store them alongside the PNG files. To make one you do
convert image.png image.mpc
and you will get out image.mpc and image.cache. Then you would simply change your code to look like this:
convert -size 600x400 xc:none \
\( 1.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 2.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 3.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 4.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 5.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 6.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
result.png
Point 3
Unfortunately you haven't answered my questions yet, but if your assets catalogue is not too big, you could put that (or the MPC equivalents above) onto a RAM disk at system startup.
Point 4
You should definitely run in parallel - that will yield the biggest gains of all. It is very simple with GNU Parallel - example here.
If you are using REDIS, it is actually easier than that. Just LPUSH your MIME-encoded images into a REDIS list like this:
#!/usr/bin/perl
################################################################################
# generator.pl <number of images> <image size in bytes>
# Mark Setchell
# Base64 encodes and sends "images" of specified size to REDIS
################################################################################
use strict;
use warnings FATAL => 'all';
use Redis;
use MIME::Base64;
use Time::HiRes qw(time);
my $Debug=0; # set to 1 for debug messages
my $nargs = $#ARGV + 1;
if ($nargs != 2) {
print "Usage: generator.pl <number of images> <image size in bytes>\n";
exit 1;
}
my $nimages=$ARGV[0];
my $imsize=$ARGV[1];
# Our "image"
my $image="x"x$imsize;
printf "DEBUG($$): images: $nimages, size: $imsize\n" if $Debug;
# Connection to REDIS
my $redis = Redis->new;
my $start=time;
for(my $i=0;$i<$nimages;$i++){
my $encoded=encode_base64($image,'');
$redis->rpush('images'=>$encoded);
print "DEBUG($$): Sending image $i\n" if $Debug;
}
my $elapsed=time-$start;
printf "DEBUG($$): Sent $nimages images of $imsize bytes in %.3f seconds, %d images/s\n",$elapsed,int($nimages/$elapsed);
and then run multiple workers that all sit there doing BLPOPs of jobs to do
#!/usr/bin/perl
################################################################################
# worker.pl
# Mark Setchell
# Reads "images" from REDIS and uudecodes them as fast as possible
################################################################################
use strict;
use warnings FATAL => 'all';
use Redis;
use MIME::Base64;
use Time::HiRes qw(time);
my $Debug=0; # set to 1 for debug messages
my $timeout=1; # number of seconds to wait for an image
my $i=0;
# Connection to REDIS
my $redis = Redis->new;
my $start=time;
while(1){
#my $encoded=encode_base64($image,'');
my (undef,$encoded)=$redis->blpop('images',$timeout);
last if !defined $encoded;
my $image=decode_base64($encoded);
my $l=length($image);
$i++;
print "DEBUG($$): Received image:$i, $l bytes\n" if $Debug;
}
my $elapsed=time-$start-$timeout; # since we waited that long for the last one
printf "DEBUG($$): Received $i images in %.3f seconds, %d images/s\n",$elapsed,int($i/$elapsed);
If I run one generator process as above and have it generate 100,000 images of 200kB each, and read them out with 4 worker processes on my reasonable spec iMac, it takes 59 seconds, or around 1,700 images/s can pass through REDIS.
The queue is being processed one at a time? Have you tried to make concurrent jobs, that will keep running in parallel so you work more than one element at once if that is the case?

How to make fade edges in image magic?

I want to create faded edges image effect using imagemagic commands. Please help me for a command to make effect as like http://postimg.org/image/h51e4twyp/
Start with Doge
curl -o doge.jpg http://i0.kym-cdn.com/photos/images/newsfeed/000/581/296/c09.jpg
Next, blur the edges.
convert doge.jpg -alpha set -virtual-pixel transparent -channel A -morphology Distance Euclidean:1,20\! +channel doge.png
You will need to output it as a .png
Imagemagick documentation
Edit: The above example is for ImageMagick v6.
For IMv7 try:
convert doge.jpg \( +clone -alpha extract -virtual-pixel black -gamma 2 +level 0,100 -white-threshold 99 -morphology Distance Euclidean:1,200! -sigmoidal-contrast 3,0% \) -compose CopyOpacity -composite doge_im7.png

Crop image on angle

I'm currently looking for a way to crop an image, but on an angle.
I don't think I can just rotate the image first as the script is supplied with specific x,y coordinates of each corner.
So if you can imaging this, image is uploaded, 1280x720.
Along with the image it's supplied with x,x coordinates for the crop zone.
However the top left and top right coordinates will not have the same y position.
Heres an examples
Before
After
Any ideas ?
You'll still need to use trigonometry methods to rotate the image, but you can mimic a crop-at-an-angle by mixing opacity copying and trimming.
First. Create an Image Mask
If all the points are giving to you, and the image size is defined, simply draw the area that needs to be extract
WIDTH=819
HEIGHT=616
TOP_LEFT=669,117
TOP_RIGHT=784,155
BOTTOM_LEFT=544,495
BOTTOM_RIGHT=659,534
convert -size $WIDTHx$HEIGHT xc:black -fill white -stroke white \
-draw "polyline $TOP_LEFT $TOP_RIGHT $BOTTOM_RIGHT $BOTTOM_LEFT" \
mask.png
Masking and Background Removal
This method of masking will turn off the alpha-channel and set the background to transparent. When we compose the two images, the resulting image will only display what's within the area we defined in the mask. (note: you may need to adjust the -background to white, or transparent.)
convert source.jpg mask.png -alpha Off -compose CopyOpacity \
-composite -background transparent copyOpacity.png
Calculate Degree to Rotate
If you have two points on a square angle, you should be able to follow the atan method. Most language will have an atan2 function. Other trigonometry questions "Rotating a rectangle" & "How to calculate the angle between two points relative to the horizontal axis?"
DELTA_Y=$(($HEIGHT-155-534))
DELTA_X=$((784-659))
DEGREE=`awk "BEGIN { pi=4.0*atan2(1.0,1.0)+90; print atan2($DELTA_Y,$DELTA_X)*180/pi; }"`
convert copyOpacity.png -rotate $DEGREE -trim final.png
Luckily, you can do everything in one step.
#!/bin/bash
WIDTH=819
HEIGHT=616
TOP_LEFT=669,117
TOP_RIGHT=784,155
BOTTOM_LEFT=544,495
BOTTOM_RIGHT=659,534
DELTA_Y=$(($HEIGHT-155-534))
DELTA_X=$((784-659))
DEGREE=`awk "BEGIN { pi=4.0*atan2(1.0,1.0)+90; print atan2($DELTA_Y,$DELTA_X)*180/pi; }"`
convert source.jpg \( -size $WIDTHx$HEIGHT xc:black -fill white -stroke white \
-draw "polyline $TOP_LEFT $TOP_RIGHT $BOTTOM_RIGHT $BOTTOM_LEFT" \) \
-alpha Off -compose CopyOpacity -composite \
-background transparent -rotate $DEGREE -trim \
final.png

Categories