I wasn’t satisfied with the outcome of my star handling program so I rewrote it. The goal was to obtain an image that is as technically accurate as possible, while contains less artifacts and it is more appealing than the original.
I recorded with the Canon 1100D mod 21×150 sec, ISO 1600, with the lens Canon EF 200mm f2.8L II USM dialed to F/4, mounted on the HEQ5. The main feature is the red glow of the ionized hydrogen region around γ Cygni (the brightest star in the frame) with dramatic gaps caused by clouds of dark nebulae. The Crescent Nebula is also there. The object is also known as NGC 6888, Caldwell 27, Sharpless 105, and is a nebula blown by the Wolf-Rayet star WR 136 (HD 192163) expected to go supernova in the next 100k years.
The program does the following: 1) removes panda eyes from around bright stars, 2) bright areas (bright stars and their halos) are replaced with black, 3) blacks get healed, 4) fainter, star like areas are replaced with black, 5) blacks get healed, 6) the original and the output-5 is mixed with a lighter than filter. Log and output files are written along the way as shown in the source code. To get the star-reduce picture, the original and the output-5 images need to be combined in a photo editing software using various filters and layering.
As I usually do, I publish the samples and the steps I took to get the final picture.
The starheal code I wrote and used
<?php class Reporter { function report($s){ $s = $s.'<br />'; $s = str_replace("<br /><br />", "<br />", $s); $s = date('Y.m.d. H:i:s ').$s; echo $s; $s = str_replace("<br />", "\r\n", $s)."\r\n"; $s = str_replace("\r\n\r\n", "\r\n", $s); file_put_contents('log.txt', $s, FILE_APPEND | LOCK_EX); } } //============================================================================ class ImageOfStarheal { public $image = false; public $image_sx = -1; public $image_sy = -1; public $pixel_buffer = array(); public $getpixel_buffer = array(); public $reporter = false; public $last_saved_to_filename = ''; public $getpixel_buffersize = 0; function __construct(){ $this->reporter = new Reporter; } function report($s){ $this->reporter->report($s); } function int2rgb($i){ if (is_array($i)){ return $i; } $r = $i >> 16; $g = ($i >> 8) %256; $b = $i % 256; return array($r, $g, $b); } function rgb2int($r, $g=false, $b=false){ if (is_array($r)){ $b = $r[2]; $g = $r[1]; $r = $r[0]; } return $r * 256*256 + $g*256 + $b; } //file operations function load_image_from_file($filename){ $this->image = imagecreatefromstring(file_get_contents($filename)); $this->image_sx = imagesx($this->image); $this->image_sy = imagesy($this->image); } function save_image_to_file($filename, $quality = 100){ $this->flushpixels(); imagejpeg($this->image, $filename, $quality); $this->last_saved_to_filename = $filename; } function last_saved_image(){ return $this->last_saved_to_filename; } //pixel operations function setpixel($x, $y, $p){ return $this->putpixel($x, $y, $p); } function putpixel($x, $y, $p){ if ($x >= $this->image_sx){ return 0; }; if ($y >= $this->image_sy){ return 0; }; if ($x < 0 ) { return 0; }; if ($y < 0 ) { return 0; }; imagesetpixel($this->image, $x, $y, $p); $this->getpixel_buffer_set($x, $y, $p); return 1; } function getpixel_buffer_set($x, $y, $p){ if ($this->getpixel_buffersize == 0){ return ; } foreach ($this->getpixel_buffer as &$get_pix){ if (($get_pix[0] == $x) AND ($get_pix[1] == $y)){ $get_pix = array($x, $y, $p); return ; } } if (count($this->getpixel_buffer) > $this->getpixel_buffersize){ $this->getpixel_buffer = array_slice($this->getpixel_buffer, round($this->getpixel_buffersize / 10), count($this->getpixel_buffer)); } $this->getpixel_buffer[] = array($x, $y, $p); } function getpixel($x, $y, $default = 0){ if ($x >= $this->image_sx){ return $default; }; if ($y >= $this->image_sy){ return $default; }; if ($x < 0 ) { return $default; }; if ($y < 0 ) { return $default; }; if ($this->getpixel_buffersize!=0){ for ($q=0; $q<count($this->getpixel_buffer); $q++){ if (($this->getpixel_buffer[$q][0] == $x) AND ($this->getpixel_buffer[$q][1] == $y)){ return $this->getpixel_buffer[$q][2]; } } }; $pix = imagecolorat($this->image, $x, $y); $this->getpixel_buffer_set($x, $y, $pix); return $pix; } function pushpixel($x, $y, $p){ //check if there is something to overwrite foreach ($this->pixel_buffer as &$pix){ if (($pix[0] == $x) AND ($pix[1] == $y)){ $pix = array($x, $y, $p); return 1; } } $this->pixel_buffer[] = array($x, $y, $p); if (count($this->pixel_buffer) > 5000){ $this->flushpixels(); } } function flushpixels(){ foreach ($this->pixel_buffer as $pix){ $this->putpixel($pix[0], $pix[1], $pix[2]); } $this->pixel_buffer = array(); } function getrgbpixel($x, $y){ return $this->int2rgb($this->getpixel($x, $y)); } function luminance($x_or_pix, $y=-1){ if ($y === -1){ if (!is_array($x_or_pix)){ $x_or_pix = $this->int2rgb($x_or_pix); } }else{ $x_or_pix = $this->getrgbpixel($x_or_pix, $y); } return floor(($x_or_pix[0] + $x_or_pix[1] + $x_or_pix[1] + $x_or_pix[2]) / 4); } function pushpoint($p, $col){ $this->pushpixel($p[0], $p[1], $col); } function filled_circle($point, $radius, $col){ // $col = 0x00FF00; if ($radius > 4){ imagefilledellipse($this->image, $point[0], $point[1], $radius, $radius, $col); return ; } if ($radius > 3){ imagefilledellipse($this->image, $point[0]+1, $point[1]+0, $radius, $radius, $col); imagefilledellipse($this->image, $point[0]-1, $point[1]+0, $radius, $radius, $col); imagefilledellipse($this->image, $point[0]+0, $point[1]-1, $radius, $radius, $col); imagefilledellipse($this->image, $point[0]+0, $point[1]+1, $radius, $radius, $col); return ; } if ($radius == 3){ imagefilledrectangle($this->image, $point[0]-$radius, $point[1]-$radius+2, $point[0]+$radius, $point[1]+$radius-2, $col); imagefilledrectangle($this->image, $point[0]-$radius+2, $point[1]-$radius, $point[0]+$radius-2, $point[1]+$radius, $col); imagefilledellipse($this->image, $point[0], $point[1], $radius, $radius, $col); } if ($radius == 2){ imagefilledrectangle($this->image, $point[0]-$radius, $point[1]-$radius+1, $point[0]+$radius, $point[1]+$radius-1, $col); imageline($this->image, $point[0]-$radius+1, $point[1]-$radius, $point[0]+$radius-1, $point[1]-$radius, $col); imageline($this->image, $point[0]-$radius+1, $point[1]+$radius, $point[0]+$radius-1, $point[1]+$radius, $col); } if ($radius == 1){ imageline($this->image, $point[0], $point[1]-1, $point[0], $point[1]+1, $col); imageline($this->image, $point[0]-1, $point[1], $point[0]+1, $point[1], $col); } if ($radius == 0){ $this->pushpoint($point, $col); } } //geometry function distance($p1, $p2){ $dx = $p1[0]-$p2[0]; $dy = $p1[1]-$p2[1]; return sqrt($dx*$dx + $dy*$dy); } function area($topleft, $bottomright){ return ($bottomright[0] - $topleft[0])*($bottomright[1]-$topleft[1]); } function pointup($p){ $p[1] = $p[1] - 1; return $p; } function pointdown($p){ $p[1] = $p[1] + 1; return $p; } function pointleft($p){ $p[0] = $p[0] - 1; return $p; } function pointright($p){ $p[0] = $p[0] + 1; return $p; } //filter function lighterthan($filename = "input.jpg"){ $i2 = imagecreatefromstring(file_get_contents($filename)); for ($x = 0; $x<imagesx($i2); $x++){ for ($y = 0; $y<imagesy($i2); $y++){ $p2 = imagecolorat($i2, $x, $y); $p1 = $this->getpixel($x, $y); if ($this->luminance($p2) > $this->luminance($p1)){ $this->putpixel($x, $y, $p2); } }; } imagedestroy($i2); } function number_with_digits($n, $d = 3){ $n = ''.$n; while (strlen($n) < $d){ $n = '0'.$n; } return $n; } }; //============================================================================ class Starheal extends ImageOfStarheal { private $context_radius_of_healing = 10; private $kill_above_this_luminosity = 170; private $kill_radius = 2; private $time_limit = 32; private $start_time = 0; private $last_file_check = 0; private $nullpix = 0; private $aroundkill_above_this_luminosity = 220; private $area_counter = 0; private $stage_counter = 0; private $heal_counter = 1; private $number_of_pixels_to_heal = 0; private $healing_inited = false; private $save_a_stage_at_every = 25000; private $stages_count = 25; function __construct($t_limit = -1){ parent::__construct(); $this->start_time = date("U"); if ($t_limit !== -1){ $this->time_limit = $t_limit; } set_time_limit($this->time_limit); ini_set('memory_limit', '2048M'); //2 giga //if called from browser, chrome's tab optimization might f things up, so close the tab ignore_user_abort(true); $this->last_file_check = $this->start_time; @unlink('output_8_premature.jpg'); $this->report('======================= START ======================'); $this->area_counter = 0; $this->stage_counter = 0; $this->number_of_pixels_to_heal = 0; } //shutdown function has_time($sec = -1, $now = -1){ if ($now === -1){ $now = date("U"); }; if ($sec === -1){ $sec = 3; } $been_running_for = $now - $this->start_time; return ($been_running_for + $sec < $this->time_limit); } function check_the_time($message=""){ $now = date("U"); if ($now - $this->last_file_check > 10){ if (!file_exists("run.txt")){ if ($message!=""){ $message = ", ".$message; } $this->premature_death("run.txt missing".$message); } $this->last_file_check = $now; } if (!$this->has_time(3, $now)){ if ($message!=""){ $message = ", ".$message; } $this->premature_death("running out of time".$message); } } function premature_death($message = ""){ $this->save_image_to_file('output_8_premature.jpg'); if ($message!=""){ $this->report("message: ".$message); }; die(); } //star operations function kill_points_with_radius($points_, $radius = -1){ $this->report('kill points: start sorting (count='.count($points_).')'); usort($points_, function ($a, $b){ if ($a[0] == $b[0]) { if ($a[1] == $b[1]){ return 0; }; return ($a[1] < $b[1]) ? -1 : 1; } return ($a[0] < $b[0]) ? -1 : 1; }); $this->report('kill points: done sorting (count='.count($points_).')'); $prev = array(-1, -1); $this->flushpixels(); if ($radius == -1){ $radius = $this->kill_radius; } $last_one = array(-3, -3); foreach ($points_ as $point){ if (!(($point[0] == $last_one[0]) AND ($point[1] == $last_one[1]))){ $this->filled_circle($point, $radius, $this->nullpix); $last_one = $point; }; }; } function is_the_pixel_color_starlike($x, $y){ $pix = $this->getpixel($x, $y, 0); $rgb = $this->int2rgb($pix); $max = max($rgb); $min = min($rgb); $luminance = $this->luminance($pix); if ($luminance > $this->kill_above_this_luminosity){ if ($max - $min > 30){ //this pixel has a color, should be kept return 0; }else{ if ($luminance > $this->aroundkill_above_this_luminosity){ return 1; }else{ return 1; }; } }else{ return 0; } } function kill_the_panda_eyes__(){ /* $above_ = $this->aroundkill_above_this_luminosity; //$above_ = 200; $below_ = 50; $margin = 1; $out =0x00FF00; $map = array(); for ($x = $margin; $x < $this->image_sx-$margin; $x++){ for ($y = $margin; $y < $this->image_sy-$margin; $y++){ $pix = $this->getpixel($x, $y, 0); $luminance = $this->luminance($pix); if ($luminance > $above_){ //go radial $old_coord_x = -1; $old_coord_y = -1; for ($angle = 0; $angle < M_PI*2; $angle = $angle + 0.1){ $line = array(); for ($radius = 1; $radius < 5; $radius++){ $coord_x = round($x + $radius*cos($angle)); $coord_y = round($y + $radius*sin($angle)); if (! (($coord_x == $old_coord_x) AND ($coord_y == $old_coord_y))){ if (! (($coord_x==$x)AND($coord_y == $y)) ){ $old_coord_x = $coord_x; $old_coord_y = $coord_y; $p = $this->getpixel($coord_x, $coord_y); if ($p!=$out){ $line[] = array($coord_x, $coord_y, $this->luminance($p)); }; } } }; if (count($line) > 3){ $prev = $line[0][2]; $drop = 0; $rize = 0; for ($q=1; $q<count($line); $q++){ if ($prev - $line[$q][2] > 180){ $drop++; } if ($line[$q][2] - $prev > 100){ $rize++; } $prev = $line[$q][2]; } if (($rize > 0) AND ($drop > 0) ){ $map[] = array($x, $y); }; } } } } } $this->kill_points_with_radius($map, 2); */ } function kill_the_panda_eyes(){ //this is a wrapper method $this->report("starting panda eye removal"); $this->kill_the_panda_eyes_(); $this->report("done panda eye removal"); } function kill_the_panda_eyes_($topleft = -1, $bottomright = -1){ $this->topleft_bottomright($topleft, $bottomright); $a = $this->area($topleft, $bottomright); if ($a > 300*300){ $wi = floor(($bottomright[0] - $topleft[0]) /2); $hi = floor(($bottomright[1] - $topleft[1]) /2); //top left $this->kill_the_panda_eyes_( array($topleft[0] + 0*$wi, $topleft[1] + 0*$hi), array($bottomright[0]- 1*$wi, $bottomright[1] - 1*$hi) ); //top right $this->kill_the_panda_eyes_( array($topleft[0] + 1*$wi, $topleft[1] + 0*$hi), array($bottomright[0]- 0*$wi, $bottomright[1] - 1*$hi) ); //bottom left $this->kill_the_panda_eyes_( array($topleft[0] + 0*$wi, $topleft[1] + 1*$hi), array($bottomright[0]- 1*$wi, $bottomright[1] - 0*$hi) ); //bottom right $this->kill_the_panda_eyes_( array($topleft[0] + 1*$wi, $topleft[1] + 1*$hi), array($bottomright[0]- 0*$wi, $bottomright[1] - 0*$hi) ); return ; } $this->report("starting to panda-eye an area (".$topleft[0].', '.$topleft[1].'), ('.$bottomright[0].', '.$bottomright[1].'), area = '.$a); $above_ = $this->aroundkill_above_this_luminosity; //$above_ = 200; $below_ = 90; $out =0x00FF00; $map = array(); for ($x = $topleft[0]; $x <= $bottomright[0]; $x++){ for ($y = $topleft[1]; $y <= $bottomright[1]; $y++){ $pix = $this->getpixel($x, $y, $out); $luminance = $this->luminance($pix); if ($luminance > $above_){ $context = 2; for ($cx = -$context; $cx <= $context; $cx++){ for ($cy = -$context; $cy <= $context; $cy++){ $cpix = $this->getpixel($x+$cx, $y+$cy, $out); if ($cpix!=$out){ $cluminance = $this->luminance($cpix); if ($cluminance < $below_){ $map[] = array($x+$cx, $y+$cy); }; }; }; } } } } $this->kill_points_with_radius($map, 1); $this->report("done panda-eying an area (".$topleft[0].', '.$topleft[1].'), ('.$bottomright[0].', '.$bottomright[1].')'); } function kill_luminous_stars(){ $this->report("start killing off the highlights ie luminous stars"); $this->flushpixels(); $star_map = array(); for ($x = 0; $x < $this->image_sx; $x++){ for ($y = 0; $y < $this->image_sy; $y++){ $s = $this->is_the_pixel_color_starlike($x, $y); if ($s > 0){ $star_map[] = array($x, $y); } /* if ($s > 1){ $er = $s-1; for ($r1 = $er; $r1 <= $er; $r1++) for ($r2 = $er; $r2 <= $er; $r2++) $star_map[] = array($x+$r1, $y+$r2); } */ } }; $this->report("done mapping highlights ie luminous stars"); $this->kill_points_with_radius($star_map, 2); $this->report("done killing off the highlights ie luminous stars"); } function kill_faint_stars(){ $this->report("start killing faint stars"); //assuming that the luminous stars have already been killed $lumidelta = 25; $max_coldelta = 45; $radius = 3; $kill = array(); $divider = 10; $progress = round($this->image_sx / $divider); $ko = 0; for ($x =0; $x < $this->image_sx; $x++){ if (($x+1) % $progress == 0){ $ko++; $this->report("no_smallerstars_".$this->number_with_digits($ko)."_of_".$this->number_with_digits($divider).".txt"); } for ($y = 0; $y < $this->image_sy; $y++){ $p = $this->getpixel($x, $y); if ($p > 0){ $rgb = $this->int2rgb($p); $average = floor(($rgb[0] + $rgb[1]*2 + $rgb[2])/4); $coldelta = 0; for ($j =0; $j<=2; $j++){ $coldelta = max($coldelta, abs($rgb[$j] - $average)); } //based on hue, this could be a star, check its context if ($coldelta < $max_coldelta){ $context = array(); $context_is_lighter = false; for ($tx = -$radius; $tx <= $radius; $tx++){ for ($ty = -$radius; $ty <= $radius; $ty++){ if ((abs($tx)==$radius)AND(abs($ty)==$radius)){ //round the corners }else{ $k = $this->getpixel($x+$tx, $y+$ty); $k_rgb = $this->int2rgb($k); $k_average = floor(($k_rgb[0] + $k_rgb[1]*2 + $k_rgb[2])/4); if ($k_average > $average){ $context_is_lighter = true; } $context[] = $k; } } } $fainter = 0; $black = 0; if (!$context_is_lighter){ $context_looks_good = 0; foreach ($context as $c) if (($c!=0)OR(true)) { if ($c ==0 ){ $black++; } $c_rgb = $this->int2rgb($c); $c_average = floor(($c_rgb[0] + $c_rgb[1]*2 + $c_rgb[2])/4); $c_coldelta = 0; for ($j =0; $j<=2; $j++){ $c_coldelta = max($c_coldelta, abs($c_rgb[$j] - $c_average)); } $is_fainter = ($c_average < $average-$lumidelta); if ($is_fainter){ $fainter++; } if (((abs($c_coldelta - $coldelta) < 15) AND (abs($c_average - $average) < 5)) OR ($is_fainter)){ $context_looks_good++; } } if ((abs(count($context) - $context_looks_good) < max(2, count($context) / 4)) AND ($fainter > 0) AND ($black < count($context) / 3)){ $kill[] = array($x, $y); } }; }//pixel is grey enough }//pixel not black } } $this->report("killing faint stars - mapping done"); $this->kill_points_with_radius($kill, $radius); $this->report("done killing faint stars"); } //healer operations function add_pixel_to_heal_context_if_fits($p1, $p2, &$context){ $d = $this->distance($p1, $p2); if ($d < $this->context_radius_of_healing){ $pix = $this->getpixel($p2[0], $p2[1], $this->nullpix); if ($pix > $this->nullpix){ $context[] = array($pix, $d); } } } function heal_a_pixel($x, $y, $min_context = 8){ if ($x >= $this->image_sx){ return 0; }; if ($y >= $this->image_sy){ return 0; }; if ($x < 0 ) { return 0; }; if ($y < 0 ) { return 0; }; if (($x === 0) OR ($x === $this->image_sx-1)){ $min_context = $min_context - 3; } if (($y === 0) OR ($y === $this->image_sy-1)){ $min_context = $min_context - 3; } $min_context = max($min_context, 3); $context = array();//pixel, distance $p = $this->getpixel($x, $y); if ($p > $this->nullpix){ return 0; } $p1 = array($x, $y); // go radially to find the first nonzero pixel for ($radius = 1; $radius <= $this->context_radius_of_healing; $radius++){ $angle_adder = 0.05; $latest_coords = array(-1, -1); if (count($context) < 10){ for ($angle = 0; $angle < 2*M_PI; $angle = $angle + $angle_adder){ $cx = round($radius * cos($angle)); $cy = round($radius * sin($angle)); $coords = array($x+$cx, $y+$cy); if (!(($cx==0)AND($cy==0))){ if (($coords[0]!=$latest_coords[0]) AND ($latest_coords[1]!=$coords[1])){ $latest_coords = $coords; $this->add_pixel_to_heal_context_if_fits($p1, $coords, $context); }; } }; }; }; //sort by distance usort($context, function ($a, $b){ if ($a[1] == $b[1]) { return 0; } return ($a[1] < $b[1]) ? -1 : 1; }); $context = array_slice($context, 0, 8); if (count($context) < $min_context){ return 0; } if (count($context) < 1){ return 0; } $dist_min = $context[0][1]; $r = array(); $g = array(); $b = array(); foreach ($context as $c){ $c[1] = $c[1] - $dist_min +1; while ($c[1] <= $this->context_radius_of_healing){ $rgb = $this->int2rgb($c[0]); $r[] = $rgb[0]; $g[] = $rgb[1]; $b[] = $rgb[2]; $c[1]++; } } $newpix = array(round(array_sum($r) / count($r)), round(array_sum($g) / count($g)), round(array_sum($b) / count($b))); $this->pushpixel($x, $y, $this->rgb2int($newpix)); return 1; } function topleft_bottomright(&$topleft, &$bottomright){ if ($topleft === false){ $topleft = array(0,0); } if ($topleft === -1){ $topleft = array(0,0); } if ($bottomright === false){ $bottomright = array($this->image_sx -1, $this->image_sy -1); }; if ($bottomright === -1){ $bottomright = array($this->image_sx -1, $this->image_sy -1); } } function count_of_pixels_to_heal($topleft = -1, $bottomright = -1){ $this->topleft_bottomright($topleft, $bottomright); $t = 0; $this->flushpixels(); for ($x = $topleft[0]; $x<=$bottomright[0]; $x++){ for ($y =$topleft[1]; $y<=$bottomright[1]; $y++){ if ($this->getpixel($x, $y) == $this->nullpix){ $t++; } } } return $t; } function heal_an_area($topleft = -1, $bottomright = -1){ $this->init_healing(); $this->topleft_bottomright($topleft, $bottomright); $a = $this->area($topleft, $bottomright); if ($a > 300*300){ $wi = floor(($bottomright[0] - $topleft[0]) /2); $hi = floor(($bottomright[1] - $topleft[1]) /2); //top left $this->heal_an_area( array($topleft[0] + 0*$wi, $topleft[1] + 0*$hi), array($bottomright[0]- 1*$wi, $bottomright[1] - 1*$hi) ); //top right $this->heal_an_area( array($topleft[0] + 1*$wi, $topleft[1] + 0*$hi), array($bottomright[0]- 0*$wi, $bottomright[1] - 1*$hi) ); //bottom left $this->heal_an_area( array($topleft[0] + 0*$wi, $topleft[1] + 1*$hi), array($bottomright[0]- 1*$wi, $bottomright[1] - 0*$hi) ); //bottom right $this->heal_an_area( array($topleft[0] + 1*$wi, $topleft[1] + 1*$hi), array($bottomright[0]- 0*$wi, $bottomright[1] - 0*$hi) ); return ; } $this->report("starting to heal area (".$topleft[0].', '.$topleft[1].'), ('.$bottomright[0].', '.$bottomright[1].'), a='.$a); $list_of_x = range($topleft[0], $bottomright[0]); $list_of_y = range($topleft[1], $bottomright[1]); $points = array(); foreach ($list_of_x as $x){ foreach ($list_of_y as $y){ $points[] = array($x, $y); } } shuffle($points); $to_heal = $this->count_of_pixels_to_heal($topleft, $bottomright); $nodelta = 0; while ($to_heal > 0){ $before = $to_heal; foreach ($points as $p){ $v = $this->heal_a_pixel($p[0], $p[1]); $to_heal = $to_heal - $v; $this->number_of_pixels_to_heal = $this->number_of_pixels_to_heal-$v; if ($v == 1){ if ($this->number_of_pixels_to_heal % $this->save_a_stage_at_every == 0){ $this->save_image_to_file('output_'.$this->heal_counter.'_stage_healed_'.$this->number_with_digits($this->stage_counter).'.jpg'); $this->stage_counter++; } } $this->check_the_time("healing_an_area"); } $this->flushpixels(); $to_heal = $this->count_of_pixels_to_heal($topleft, $bottomright); $delta = $before - $to_heal; if ($delta == 0){ $nodelta++; }else{ $nodelta = 0; } if ($nodelta > 2){ $to_heal = 0; $this->report("exiting healing round"); } }; $this->flushpixels(); $this->report("done healing area (".$topleft[0].', '.$topleft[1].'), ('.$bottomright[0].', '.$bottomright[1].')'); $this->report("count of pixels to heal = ".$this->number_of_pixels_to_heal); // $this->save_image_to_file('output_1_area_healed_'.$this->number_with_digits($this->area_counter).'.jpg'); $this->area_counter++; } function init_healing($force = false){ if (!$force){ if ($this->healing_inited){ return ; } }else{ $this->heal_counter++; }; $this->healing_inited = true; $this->number_of_pixels_to_heal = $this->count_of_pixels_to_heal(); $this->save_a_stage_at_every = round($this->number_of_pixels_to_heal / $this->stages_count); $this->report("count of pixels to heal = ".$this->number_of_pixels_to_heal); } function process_image($filename = "input.jpg"){ $this->load_image_from_file($filename); $this->kill_the_panda_eyes(); $this->save_image_to_file("input_1_no_panda.jpg"); $this->kill_luminous_stars(); $this->save_image_to_file("input_2_no_luminous_stars.jpg"); $this->init_healing(true); $this->heal_an_area(); $this->save_image_to_file("output_z1_healed.jpg"); $this->kill_faint_stars(); $this->save_image_to_file("output_z2_no_faint_stars.jpg"); $this->init_healing(true); $this->heal_an_area(); $this->save_image_to_file("output_z3_healed.jpg"); $this->lighterthan($filename); $this->save_image_to_file("output_z4_lighterthan.jpg"); } } ?>
Usage example
<?php require_once("starheal.php"); $h = new Starheal(86400); $h->process_image(); ?> <img src="input.jpg" id="i0" /> <img src="<?php echo $h->last_saved_image(); ?>?<?php echo mt_rand(); ?>" id="i1" style="display:none" /> <script> setInterval(function (){ var i1 = document.getElementById("i0"); var i2 = document.getElementById("i1"); if (i1.style.display==""){ i1.style.display = "none"; i2.style.display = ""; }else{ i2.style.display = "none"; i1.style.display = ""; } }, 1000); </script>





