PHP Image Zoom - Exponential scale - php

Iam trying to calculate a zoom Effect beetween 12 Images. Every Image is 100% larger then the one before. Its geting near to perfect, but there is only a issue at the transition beetween the images. It isn't fluid zoom beetween each image.
Please see the video: http://youtu.be/dUBbDjewpO0
I think the Exponential expression pow() isnt coorect for some reason.
Here is the PHP script, but i cant find the issue:
<?php
$imageFiles=array(
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg');
$targetFrameRate=$targetDuration='18';
$imageCount = count($imageFiles);
$totalFrames = ($targetFrameRate*$targetDuration);
$sourceIndex = 0;
$firstIndex = 1;
$lastIndex = $totalFrames; //==total frames
$currentScale = 1;//image scaling for first scale
$deltaScale = ((($imageCount-1)*($scaleFactor-$currentScale))/$totalFrames);
for ($i=$firstIndex; $i<=$lastIndex; $i++) {
// prepare filename
$filename = createImageFilename($i, $imageType);
// determine source..
if ($i == $firstIndex) {
$newSourceIndex = 0;
}
else if ($i == $lastIndex) {
$newSourceIndex = ($imageCount-1);
}
else {
$newSourceIndex = intval(($i*($imageCount-1))/$totalFrames);
}
// create frame..
if ($newSourceIndex != $sourceIndex) {
$sourceIndex = $newSourceIndex;
$currentScale = pow($scaleFactor, $sourceIndex);
$nextScale = pow($scaleFactor, ($sourceIndex+1));
$deltaScale = ((($imageCount-1)*($nextScale-$currentScale))/$totalFrames);
copyImage($imageFiles[$sourceIndex],
sprintf('%s/%s', $outputDir, $filename),
$imageWidth,
$imageHeight,
$imageType);
}
else {
createImage($imageFiles[$sourceIndex],
sprintf('%s/%s', $outputDir, $filename),
($currentScale/pow($scaleFactor, $sourceIndex)),
$imageWidth,
$imageHeight,
$imageType);
}
//DEBUG: buffer some values for optional debug-output
if (isDebugOutputEnabled()) {
$debug_idx[$i] = $filename;
$debug_inf[$i] = sprintf('sourceIndex=%d , scale=%01.2f<br />', $sourceIndex, $currentScale);
}
// advance..
$currentScale += $deltaScale;
}
?>
rendering is well
shell_exec('ffmpeg -f image2 -i /var/www/htdocs/image2/i%d.jpg -s 1280x720 -movflags faststart -b:v 5500k -r 18 output.flv');

The problem comes from the fact that you are adding a delta to your scale instead of multiplying it by a constant amount each frame:
$currentScale += $deltaScale;
An exponential zoom means you increase the zoom by a constant factor (not difference) for a given constant amount of time, so you need to change that line to:
$currentScale *= $deltaScale;
and also calculate $deltaScale differently:
$deltaScale = pow($nextScale / $currentScale, ($imageCount-1) / $totalFrames);
This will compute a fractional power of the scale difference between the images, so that when you multiply it with the $currentScale value $totalFrames / ($imageCount-1) times (the number of frame you render between the current scale and next scale), the result will be an increase by a factor of $nextScale / $currentScale.
Simplification:
Because the zoom is at a constant rate for the whole animation, $deltaScale is constant the whole time, so you can compute it outside the loop like this:
$deltaScale = pow($scaleFactor, ($imageCount-1) / $totalFrames);

Related

Fastest way to compute & feed an arbitrary color centroid in an image to PHP

I'm looking for the fastest way to compute a directional vector based on an arbitrary color in an image (a Rpi camera, but a JPEG file for testing is OK for now), a.k.a. tracking a colored ball project. Please note that the resulting vector (or centroid coordinates, whatever) needs to be passed to PHP for the program execution, so the solution I'm looking for needs to end with PHP, but can be anything before, given it can be implemented on both Windows and Linux.
Consider an input JPEG image:
Here are 2 example directional vectors I'm after, obtained based on a 1) teal color input and 2) purple color input. Obviously, only 1 vector will ever be asked at a time, I put 2 to demonstrate multiple examples into 1 image, but it's always gonna be only 1 vector at a time. Note that the resulting vectors ("v") are standardized to -1.0 (bottom/left) to +1.0 (bottom/right) so that zero is the middle of the picture.
Here are the various solutions I've implemented/tested so far and how much time the whole process takes, based on a 960x640 JPEG picture, but the implemented solution will be tied to a Rpi camera input, I do not have the camera yet so I use a JPEG image until the camera arrives from China.
1) 2700ms : Use GD2 that is bundled with PHP, for loop over each pixels, push pixels matching ~10% RGB values in XY arrays, average the XY arrays, compute/normalize directional vector from XY arrays.
$arr_matching_pixels = array('arr_x' => array(), 'arr_y' => array());
for($y = 0; $y < $h - 1; $y++){
for($x = 0; $x < $w - 1; $x++){
$arr_pixel = imagecolorsforindex($img, imagecolorat($img, $x, $y));
if(abs($arr_pixel['red'] - $arr_seek_color['red']) < 30){
if(abs($arr_pixel['green'] - $arr_seek_color['green']) < 30){
if(abs($arr_pixel['blue'] - $arr_seek_color['blue']) < 30){
array_push($arr_matching_pixels['arr_x'], $x);
array_push($arr_matching_pixels['arr_y'], $y);
}
}
}
}
}
// Compute centroid of color... etc...
2) 700ms : Same as #1 except begin by resizing the canvas by 50% (acceptable loss) using imagecreatefromjpeg('_test_cam_img.jpg');
3) 560ms : Same as #2 except use ImageMagick with a pixel iterator loop to read the pixels
$imagick = new Imagick(realpath($o_img));
$arr_matching_pixels = array('arr_x' => array(), 'arr_y' => array());
$arr_pixel = array();
$iterator = $imagick->getPixelIterator();
foreach($iterator as $y => $pixels){
foreach($pixels as $x => $pixel){
$arr_pixel = $pixel->getColor();
if(abs($arr_pixel['r'] - $arr_seek_color['red']) < 30){
if(abs($arr_pixel['g'] - $arr_seek_color['green']) < 30){
if(abs($arr_pixel['b'] - $arr_seek_color['blue']) < 30){
array_push($arr_matching_pixels['arr_x'], $x);
array_push($arr_matching_pixels['arr_y'], $y);
}
}
}
}
}
// Compute centroid of color... etc...
4) 340ms : Call the system's ImageMagick binary via the exec() function, pass it the image location, the chroma/color key, a resize by 50% param, a 10% fuzz param, and the sparse-color: modifier to extract a textual (CSV-like) list representation of desired pixels, then use PHP to loop over each line, explode commas and push all pixels in XY arrays, average the XY arrays, compute/normalize directional vector from XY arrays. I noted that calling exec() proves to be quite slower than executing the same command directly from the Windows command line.
$imagick = new Imagick(realpath($o_img));
$out = exec('"E:\Users\Ben\Roaming Apps\imagemagick-6.9.3\convert" E:\wamp64\www\test_cam_img.jpg -resize 50% -fuzz 10% +transparent rgb(' . $arr_seek_color['red'] . ',' . $arr_seek_color['green'] . ',' . $arr_seek_color['blue'] . ') sparse-color:');
$arr_lines = explode(' ', $out);
$arr_matching_pixels = array('arr_x' => array(), 'arr_y' => array());
foreach($arr_lines as $str_line){
$arr_xy_coords = explode(',', $str_line);
array_push($arr_matching_pixels['arr_x'], $arr_xy_coords[0]);
array_push($arr_matching_pixels['arr_y'], $arr_xy_coords[1]);
}
// Compute centroid of color... etc...
5) 32ms : PHP creates an "in" text file containing the image path and the chroma/color key and begins looping until it reads an "out" text file. A python+OpenCV script already/always runs a (stoppable) infinite loop constantly looking for an "in" text file and when it exists, it read it, explodes the values, makes a 1-bit mask using the HSV values ~10% (cv2.inRange) from the "in" file, then makes an array using cv2.findNonZero(mask) and computes the array mean value and writes it to an "out" text file that PHP immediately reads, containing the directional vector value. This is by far, the fastest way I have found, but it is awkward, because it implies that the python script will have to be programmed in a CRONJOB and monitored/relaunched in a single instance if it crashes.
file_put_contents('_avg_color_coords_in.txt', $o_img . "\n" . $arr_seek_color['h'] . ',' . $arr_seek_color['s'] . ',' . $arr_seek_color['l']);
$starttime = time();
while((time() - $starttime) < 5){ // Max 5 seconds (exaggerated)
if(file_exists('_avg_color_coords_out.txt')){
$dir_vector = (float) file_get_contents('_avg_color_coords_out.txt');
if(!#unlink('_avg_color_coords_out.txt')){
sleep(1);
unlink('_avg_color_coords_out.txt');
}
break;
}
usleep(2000);
}
// $dir_vector ("v", the centroid of the color) is already computed by Python
// ---------- PYTHON SCRIPT ----------
import math
import cv2
import numpy as np
import os
import time
#cap = cv2.VideoCapture(0)
#while (1):
# _, frame = cap.read()
if(os.path.exists('_avg_color_coords_stop.txt')):
exit()
while not os.path.exists('_avg_color_coords_in.txt'):
time.sleep(0.002)
f = open('_avg_color_coords_in.txt', 'r')
imgsrc = f.readline().rstrip('\n')
rgbcol = [int(x) for x in f.readline().rstrip('\n').split(',')]
frame = cv2.imread(imgsrc)
h, w = frame.shape[:2]
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hfacl = rgbcol[0] / 360 * 180 * 0.95
hfach = rgbcol[0] / 360 * 180 * 1.05
sfacl = rgbcol[1] / 100 * 255 * 0.9
sfach = rgbcol[1] / 100 * 255 * 1.1
vfacl = rgbcol[2] / 100 * 255 * 0.9
vfach = rgbcol[2] / 100 * 255 * 1.1
lower_color = np.array([hfacl, sfacl, vfacl]) # 0..180, 0..255, 0..255 not percentage!
upper_color = np.array([hfach, sfach, vfach]) # 0..180, 0..255, 0..255 not percentage!
mask = cv2.inRange(hsv, lower_color, upper_color)
#cv2.imshow('mask', mask)
points = cv2.findNonZero(mask)
if(points.any()):
avg = np.mean(points, axis=0)
else:
avg = [0,0]
#print(avg)
v = -math.atan(((w * 0.5) - avg[0][0]) / (h - avg[0][1])) / (3.1415 * 0.5);
f2 = open('_avg_color_coords_out.txt', 'w+')
f2.write("%s" % str(v))
# k = cv2.waitKey(5) & 0xff
# if k == 27:
# break
#cv2.destroyAllWindows()
#cap.release()
f2.close()
f.close()
os.remove('_avg_color_coords_in.txt')
6) 38ms : Same as #5 except begin by resizing the canvas by 50% (acceptable loss) which doesn't seem to speed up things at all, and even seems counterproductive a little bit.
Is there a faster way or is this optimal? This will run every second on a 900mhz Rpi, so it needs to be quick. I think 30ms on a 900mhz CPU will be around 150-200ms (not tested yet, waiting for the camera to ship)
I had a quick go in php-vips:
#!/usr/bin/env php
<?php
require __DIR__ . '/vendor/autoload.php';
use Jcupitt\Vips;
$image = Vips\Image::newFromFile($argv[1], ['access' => 'sequential']);
# Target colour in RGB.
$target = [50, 10, 100];
# Select pixels where all bands are less than 10 away from the target.
# (and render it to memory ... we'll be reusing this mask image).
# The mask image will have one band with 0 for false and 255 for true.
$mask = $image->subtract($target)->abs()->less(10)->bandand()->copyMemory();
# The number of set pixels in the mask.
$n_set = $mask->avg() * $mask->width * $mask->height / 255;
# Handy for debugging: uncomment to write the mask image for inspection.
# $mask->writeToFile("x.png");
# Make a two-band image where band 0 is x coordinates and band 1 is y
# coordinates.
$coords = Vips\Image::xyz($mask->width, $mask->height);
# Make an indexed histogram: sum $coords at each position.
$pos = $coords->hist_find_indexed($mask);
# fetch the sum of the 255 value (true) pixels
[$x_sum, $y_sum] = $pos->getpoint(255, 0);
echo("x = " . $x_sum / $n_set . "\n");
echo("y = " . $y_sum / $n_set . "\n");
I can run it like this:
$ time ./locate-rgb.php ~/pics/x.jpg
x = 483.375
y = 487.75
real 0m0.079s
user 0m0.085s
sys 0m0.022s
So about 80ms on this modest laptop. That includes PHP startup and shutdown, and decompressing the JPG image.
That's only going to work in very constrained lighting and camera setups, but perhaps that's OK? It would be easy to make the ball detection fancier, but of course it would slow it down a bit.

randomly generating colors with php

So I'm working on making my header change color everyday, and I was attempting to create this using a random color. There are 2 colors in the header and I am making them complimentary colors. the first color is generated randomly, and then the second is modified by changing the Hue via 150`. The problem is when certain colors are chosen, they could be either too vibrant or dark. I have a check running so that I can slightly control the brightness value, but there are still some colors that are too bright ( for instance extreme yellows ). I'l post my code below. Any help or suggestions is appreciated! Thanks!
// grab a random color on hue
$h = rand(0,360);
// color values 50-120 tend to be extremely bright,
// make adjustments to the S and L accordingly
// a better solution is available?
if ($h > 50 && $h < 120) {
$s = rand(60,80);
$l = rand(30,50);
} else {
$s = rand(60,90);
$l = rand(38,63);
}
// declare string to place as css in file for primary color
$randomColor = "hsl(". $h .",". $s ."%,". $l ."%)";
// declare degree for secondary color (30 = analogous, 150 = complimentary)
$degree = 150;
// point to secondary color randomly on either side of chart
$bool = rand(0,1);
if ($bool) {
$x = $degree;
} else {
$x = -$degree;
}
// set value of the new hue
$nh = $h + $degree;
// if the new hue is above 360 or below 0, make adjustments accordingly
if ($nh > 360) {
$nh -= 360;
}
if ($nh < 0 ) {
$nh = 360 - $nh;
}
// set the secondary color
$secondaryColor = "hsl(". abs($h + $x) .",". $s ."%,". $l ."%)";
This seems very simple and I'm sure there is a better method. I looked around, but all I noticed were the basic formula's via degrees for the hue etc. Thanks again!
This is really more of a question of which colors you deem acceptable for viewing. This certainly isn't an optimal solution but it's an approach that is readable at least (it's also slightly more random than your original, if you even care about that):
function randColor() {
return array( rand(0,360), rand(0,100), rand(0,100) );
}
function isAcceptableColor($colorArr) {
// return true if the color meets your criteria
}
do {
$color = randColor();
} while ( ! isAcceptableColor($color) );

check image for corruption [duplicate]

I'm using Curl via Proxies to download images with a scraper I have developed.
Unfortunately, it gets the odd image which looks like these and the last one is completely blank :/
When I test the images via imagemagick (using identify) it tells me they are valid images.
When I test the images via exif_imagetype() and imagecreatefromjpeg() again, both these functions tell me the images are valid.
Does anyone have a way to determine if the image has majority of greyness or is completely blank/white and these are indeed corrupted images?
I have done a lot of checking with other questions on here, but I haven't had much luck with other solutions. So please take care in suggesting this is a duplicate.
Thanks
After knowing about imgcolorat, I did a search and stumbled on some code. I came up with this:
<?php
$file = dirname(__FILE__) . "/images/1.jpg";
$img = imagecreatefromjpeg($file);
$imagew = imagesx($img);
$imageh = imagesy($img);
$xy = array();
$last_height = $imageh - 5;
$foo = array();
$x = 0;
$y = 0;
for ($x = 0; $x <= $imagew; $x++)
{
for ($y = $last_height;$y <= $imageh; $y++ )
{
$rgb = #imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
if ($r != 0)
{
$foo[] = $r;
}
}
}
$bar = array_count_values($foo);
$gray = (isset($bar['127']) ? $bar['127'] : 0) + (isset($bar['128']) ? $bar['128'] : 0) + (isset($bar['129']) ? $bar['129'] : 0);
$total = count($foo);
$other = $total - $gray;
if ($gray > $other)
{
echo "image corrupted \n";
}
else
{
echo "image not corrupted \n";
}
?>
Anyone see some potential pitfalls with this? I thought about getting the last few rows of the image and then comparing the total of r 127,128,129 (which are gray) against the total of other colours. If gray is greater than the other colours then the image is surely corrupted.
Opinions welcome! :)
found this page when looking for a way to check visually corrupted images like this. Here is a way to solve the problem using bash (anyway, the convert command line can be easily adapted for php or python) :
convert INPUTFILEPATH -gravity SouthWest -crop 20%x1% -format %c -depth 8 histogram:info:- | sed '/^$/d' | sort -V | head -n 1 | grep fractal | wc -l
It crops a little square in the southwest corner of the picture, then gets the histogram of this picture. If the main color of the histogram has the name "fractal" instead of an rgb color, it means this zone is corrupted and so the output will be 1 and 0 otherwise.
Hope this helps!
If the image it is returning is a valid file, then I would recommend running the scrape twice (ie. download it twice and check to see if they are the same).
Another option would be to check the last few pixels of the image (ie. bottom-right corner) to see if they match that color of grey exactly. If they do, then redownload. (obviously this approach fails if you download an image that is actually supposed to be grey in that corner, in that exact colour...but if you check several of the last pixels it should reduce the chance of that to an acceptable level).
I use this one. If the most of pixels in right bottom corner (5x5) are grey, then image is broken.
define('MIN_WIDTH',500);
define('MIN_HEIGHT',200);
function isGoodImage($fn){
list($w,$h)=getimagesize($fn);
if($w<MIN_WIDTH || $h<MIN_HEIGHT) return 0;
$im=imagecreatefromstring(file_get_contents($fn));
$grey=0;
for($i=0;$i<5;++$i){
for($j=0;$j<5;++$j){
$x=$w-5+$i;
$y=$h-5+$j;
list($r,$g,$b)=array_values(imagecolorsforindex($im,imagecolorat($im,$x,$y)));
if($r==$g && $g==$b && $b==128)
++$grey;
}
}
return $grey<12;
}
ImageMagick's identify command will identify far more corrupt images if you call it with the -verbose option. And there's a -regard-warnings option as well, which will make it treat warnings as errors. Try these against a bad image, and see if the result is a non-zero error code.

Setting compression level in Imagick automatically to reach max file size

I am retrieving the size of images as they are uploaded Imagick:
$im->getImageSize();
This returns the size in bytes of the image.
I would like to set the compression level automatically so that the file size never goes above a certain size. If I wanted to limit to 70kb with a minimum allowed compression level of 60 (on a scale of 0-100) I would start by doing something like this:
public function getCompLevel($size)
{
$maxsize = 70000; // Set rough max size of file
$mincomp = 60; // Set minimum compression level allowed
if($size > $maxsize ){ // If file size exceeds max allowed size, perform calculation
$comp = **EQUATION**
}
return ($comp < $mincomp) ? $mincomp : $comp; // if output is less than minimum allowed compression , return minimum. If not return calculated compression level
}
What I am trying to figure out is the equation needed to calculate a close approximation of the needed compression level based on the files size. I understand this may not be all that accurate due to colours effecting file size, but I would like to get as close as possible.
Any help would be greatly appreciated.
I like this question, although there is really no right answer. I have replicated multipule instances in which the variable $x would represent the file size starting at zero and would increase to double the maximum file size. Additionally, I have created a variable $equalizer. This variable works exponentially, whereas setting it to 100 will create a higher compression level, and alternatively the closer to 0 will create a much larger gap.
<?php
$max_file_size = 70000;
$max_compression = 60;
$equalizer = 100;
for($x=0;$x<$max_file_size+$max_file_size;$x+=10000){
if($x < $max_file_size){
echo $max_compression.'<br>';
}else{
echo $max_compression - (($x / $max_compression * $max_file_size) / ($max_file_size * $max_compression * $equalizer)).'<br>';
}
}?>
In, your real world situation, I would imagine your function looking something like:
<?php
function getCompLevel($size){
$maxsize = 70000;
$compression = 60;
$equalizer = 100;
if($size > $maxsize ){
$compression = $compression - (($size / $compression * $maxsize) / ($maxsize * $compression * $equalizer));
}
return $compression;
}?>

PHP: Determine Visually Corrupted Images (yet valid) downloaded via Curl with GD/Imagemagick

I'm using Curl via Proxies to download images with a scraper I have developed.
Unfortunately, it gets the odd image which looks like these and the last one is completely blank :/
When I test the images via imagemagick (using identify) it tells me they are valid images.
When I test the images via exif_imagetype() and imagecreatefromjpeg() again, both these functions tell me the images are valid.
Does anyone have a way to determine if the image has majority of greyness or is completely blank/white and these are indeed corrupted images?
I have done a lot of checking with other questions on here, but I haven't had much luck with other solutions. So please take care in suggesting this is a duplicate.
Thanks
After knowing about imgcolorat, I did a search and stumbled on some code. I came up with this:
<?php
$file = dirname(__FILE__) . "/images/1.jpg";
$img = imagecreatefromjpeg($file);
$imagew = imagesx($img);
$imageh = imagesy($img);
$xy = array();
$last_height = $imageh - 5;
$foo = array();
$x = 0;
$y = 0;
for ($x = 0; $x <= $imagew; $x++)
{
for ($y = $last_height;$y <= $imageh; $y++ )
{
$rgb = #imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
if ($r != 0)
{
$foo[] = $r;
}
}
}
$bar = array_count_values($foo);
$gray = (isset($bar['127']) ? $bar['127'] : 0) + (isset($bar['128']) ? $bar['128'] : 0) + (isset($bar['129']) ? $bar['129'] : 0);
$total = count($foo);
$other = $total - $gray;
if ($gray > $other)
{
echo "image corrupted \n";
}
else
{
echo "image not corrupted \n";
}
?>
Anyone see some potential pitfalls with this? I thought about getting the last few rows of the image and then comparing the total of r 127,128,129 (which are gray) against the total of other colours. If gray is greater than the other colours then the image is surely corrupted.
Opinions welcome! :)
found this page when looking for a way to check visually corrupted images like this. Here is a way to solve the problem using bash (anyway, the convert command line can be easily adapted for php or python) :
convert INPUTFILEPATH -gravity SouthWest -crop 20%x1% -format %c -depth 8 histogram:info:- | sed '/^$/d' | sort -V | head -n 1 | grep fractal | wc -l
It crops a little square in the southwest corner of the picture, then gets the histogram of this picture. If the main color of the histogram has the name "fractal" instead of an rgb color, it means this zone is corrupted and so the output will be 1 and 0 otherwise.
Hope this helps!
If the image it is returning is a valid file, then I would recommend running the scrape twice (ie. download it twice and check to see if they are the same).
Another option would be to check the last few pixels of the image (ie. bottom-right corner) to see if they match that color of grey exactly. If they do, then redownload. (obviously this approach fails if you download an image that is actually supposed to be grey in that corner, in that exact colour...but if you check several of the last pixels it should reduce the chance of that to an acceptable level).
I use this one. If the most of pixels in right bottom corner (5x5) are grey, then image is broken.
define('MIN_WIDTH',500);
define('MIN_HEIGHT',200);
function isGoodImage($fn){
list($w,$h)=getimagesize($fn);
if($w<MIN_WIDTH || $h<MIN_HEIGHT) return 0;
$im=imagecreatefromstring(file_get_contents($fn));
$grey=0;
for($i=0;$i<5;++$i){
for($j=0;$j<5;++$j){
$x=$w-5+$i;
$y=$h-5+$j;
list($r,$g,$b)=array_values(imagecolorsforindex($im,imagecolorat($im,$x,$y)));
if($r==$g && $g==$b && $b==128)
++$grey;
}
}
return $grey<12;
}
ImageMagick's identify command will identify far more corrupt images if you call it with the -verbose option. And there's a -regard-warnings option as well, which will make it treat warnings as errors. Try these against a bad image, and see if the result is a non-zero error code.

Categories