NGC 7000 as I processed it on 2016-09-22

NGC 7000 as I processed it on 2016-09-22

Back in December 2015 my modded camera had the first Ha-enhanced light. As I keep most raws, I dag up those in order to test whether my processing abilities have evolved. One aspect is the healing algorithm I wrote to handle the dense star fields – ie: remove all highlights and their immediate context and fill the holes from the remaining image data. Canon 1100D mod, 20×110 sec, ISO 1600, Canon 200mm f/2.8 lens dialed to f/4.5, EQ3 mount, unguided. More than fairly dark sky of a place near Dangau Mare, Cluj county, Romania. 

a raw sample

a raw sample

stack

stack

2015 interpretation

2015 interpretation

July 2016 interpretation

July 2016 interpretation

September 2016: a very careful stack and some histogram adjustment

September 2016: a very careful stack and some histogram adjustment

Before removing the stars

Before removing the stars

The holes – the stars and their context is black

The holes – the stars and their context is black

Holes punched in place of the stars, then (mostly) healed

Holes punched in place of the stars, then (mostly) healed

The star-removal of the 2016-09-22 branch

The star-removal of the 2016-09-22 branch

Recombining the nebula with the old stars

Recombining the nebula with the old stars

stage

stage

almost done, crop needed

almost done, crop needed

The 2016-09-17 branch: NGC 7000

The 2016-09-17 branch: NGC 7000

NGC 7000, The 2016-09-22 reprocess with a modified code, final picture

NGC 7000, The 2016-09-22 reprocess with a modified code, final picture

Finally, the process

the 2015 december take

the 2015 december take

The 2016 july take

The 2016 july take

The 2016-09-17

The 2016-09-17

The 2016-09-22

The 2016-09-22

 

The code I used to process this time

<!-- 
script to fill the blanks of input.jpg 
by extrapolating the context of the blank, 
saving the output as output*.jpg
-->
<div align="center">
<?php

report("=============starting the script=============");


$i = imagecreatefromstring(file_get_contents('input.jpg'));

report("created the input image");

//settings
$GLOBALS['minlumi'] = 35;
$GLOBALS['maxlumi'] = 140;
$GLOBALS['max_damage'] = 250;
$GLOBALS['csillaglumi'] = 150;
$GLOBALS["kill_highlight_radius"] = 2;  //2 or 3 

$stages_to_save = 25;

set_time_limit(60000);//majdnem 24 ora :P
ini_set('memory_limit', '2048M'); //2 giga, szoval legyszives
ignore_user_abort(true);

//========================================================================
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);
}
//========================================================================
function numberaskilo($i){
   while (strlen($i) < 3){
      $i = "0".$i;
   }
   return $i;
}
//========================================================================
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;
}
//========================================================================
function luminanceof($i_or_rgb){
   if (!is_array($i_or_rgb)){
      $i_or_rgb = int2rgb($i_or_rgb);
   }
   return floor(($i_or_rgb[0] + $i_or_rgb[1]*2 + $i_or_rgb[2]) / 4); 
}
//========================================================================
function imagesetpixel_($i, $tx, $ty, $p){
   if (($tx >= 0) && ($tx < imagesx($i)) && ($ty >= 0) && ($ty < imagesy($i))){
      imagesetpixel($i, $tx, $ty, $p);
   };
}
//========================================================================
function imagecolorat_($i, $tx, $ty){
   if (($tx >= 0) && ($tx < imagesx($i)) && ($ty >= 0) && ($ty < imagesy($i))){
      return imagecolorat($i, $tx, $ty);
   };
   return 0;
}
//========================================================================
function maxcolororluminance($pix){
   return luminanceof($pix);
   ////
   $pix  = int2rgb($pix);
   $pix[] = floor(($pix[0] + $pix[1] + $pix[1] + $pix[2]) / 4); 
   return max($pix);
}
//========================================================================
function heal_pixel_if_possible($i, $x, $y){
       $xy = $x.'_'.$y;
       $pix = imagecolorat($i, $x, $y);
       if (maxcolororluminance($pix) >= $GLOBALS['minlumi']){
           return false;
       };
       $u = array();
       for ($tx = -1; $tx < 2; $tx++){
          for ($ty = -1; $ty < 2; $ty++){
             if (($tx==0)&&($ty==0)){
                // _self
             }else{
                $p = imagecolorat_($i, $tx+$x, $ty+$y);
                $l = maxcolororluminance($p);
                if (($l < $GLOBALS['maxlumi']) && ($l >= $GLOBALS['minlumi'])){
                   $u[] = int2rgb($p);
                }
             };   
          }
       }
       
       if (count($u) >= $GLOBALS['mincontext']){
          $uj = array(0,0,0);
          foreach ($u as $j){
             for ($q=0; $q<3; $q++){
                $uj[$q] = $uj[$q] + $j[$q];
             };                
          }
          for ($q=0; $q<3; $q++){
             $uj[$q] = floor($uj[$q] / count($u));
          };         
          imagesetpixel($i, $x, $y, rgb2int($uj));          
          return true;
       }
}
//========================================================================
function handle_this_pixel(&$i, &$x, &$y, &$healed, &$save_stage_at_x_heals, &$st){
   if (!file_exists("run.txt")){
      imagejpeg($i, "output_8_premature.jpg", 95);
      die();
   }
   if (heal_pixel_if_possible($i, $x, $y)){
      $healed++;
      if ($healed % $save_stage_at_x_heals == 0){
          imagejpeg($i, 'output_3_stage_'.numberaskilo($st).'.jpg', 95);
          $st++;
      }
   };
};
//========================================================================
function kill_starmap(&$i, &$star_map, $radius = -1){
   if ($radius == -1){   
      $radius = $GLOBALS["kill_highlight_radius"];
   };      
   foreach ($star_map as $star){
      $min = -$radius;
      $max = $radius;
      for ($rx = $min; $rx <= $max; $rx++){
         for ($ry = $min; $ry <= $max; $ry++){                   
            if (
                (($rx == $min) OR ($rx == $max)) 
                AND 
                (($ry == $min) OR ($ry == $max)) 
               ){
               //this is a corner, but lets make the hole a bit round
            }else{
               imagesetpixel_($i, $star[0]+$rx, $star[1]+$ry, 0);
            }
         }
      }
   }
}
//========================================================================
function kill_off_the_highlights(&$i){
   report("killing off the highlights");
   $star_map = array();
   for ($x = 0; $x < imagesx($i); $x++){
      for ($y = 0; $y < imagesy($i); $y++){
          $pix = imagecolorat($i, $x, $y);
          if (maxcolororluminance($pix) > $GLOBALS['csillaglumi']){
             $star_map[] = array($x, $y);
          };
      }
   }
   kill_starmap($i, $star_map);
   
   imagejpeg($i, "input_2_nohighlights.jpg", 100);
   report('done killing off the highlights');
};
//========================================================================
function kill_off_remaining_starlike_areas(&$i){
   ////ha minden iranyba ugyanaz vagy kisebb, akkor jelolt
   $lumidelta = 25;
   $max_coldelta = 45;
   $radius = 3;
   $kill = array();
   $oszto = 10;
   $progress = round(imagesx($i) / $oszto);
   $ko = 0;
   for ($x =0; $x < imagesx($i); $x++){
      if (($x+1) % $progress == 0){
           $ko++;
           file_put_contents("input_4_no_smallerstars_".numberaskilo($ko)."_of_".numberaskilo($oszto).".txt", 100);              
      }
      if (!file_exists('run.txt')){
         die("premature exit");
      }
      for ($y = 0; $y < imagesy($i); $y++){
         $p = imagecolorat($i, $x, $y);
         if ($p > 0){
             $rgb = int2rgb($p);
             $atlag = floor(($rgb[0] + $rgb[1]*2 + $rgb[2])/4);
             $coldelta = 0;
             for ($j =0; $j<=2; $j++){
                $coldelta = max($coldelta, abs($rgb[$j] - $atlag));
             }
             //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 = imagecolorat_($i, $x+$tx, $y+$ty);
                         $k_rgb = int2rgb($k);
                         $k_atlag = floor(($k_rgb[0] + $k_rgb[1]*2 + $k_rgb[2])/4);
                         if ($k_atlag > $atlag){
                            $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 = int2rgb($c);
                        $c_atlag = 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_atlag));
                        }
                        
                        $is_fainter = ($c_atlag < $atlag-$lumidelta);
                        if ($is_fainter){
                           $fainter++;
                        }
                        if (((abs($c_coldelta - $coldelta) < 15) AND (abs($c_atlag - $atlag) < 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    
      }
   }
   kill_starmap($i, $kill, $radius);
   imagejpeg($i, "input_6_no_smallerstars.jpg", 100);   
}
//========================================================================
function try_loading_a_cache_if_exists(&$i){
    $icv = 'input_9_carved.jpg';
    
    //remove this cache, start from scratch
    if (file_exists($icv)){
       unlink($icv);
    }
    
    if (!file_exists($icv)){
       report('image input loaded, no cache<br />');
       
       
       kill_off_the_highlights($i);
       
       kill_off_remaining_starlike_areas($i);

       report('saving the carved version<br />');
       imagejpeg($i, $icv, 100);
       
       foreach (glob("*.jpg") as $filename){
          echo '<img src="'.$filename.'?r='.mt_rand().'" />';
       }       
       //die();
    }else{
       report('image outpIt loaded from cache<br />');
       imagedestroy($i);
       $i = imagecreatefromstring(file_get_contents($icv));
    }
}
function filter_lighterthanoriginal(&$i){
    //apply a "lighter than" filter, assuming the "to be replaced" areas were black
    $o = imagecreatefromstring(file_get_contents("input.jpg"));
    for ($x = 0; $x< imagesx($o); $x++){
       for ($y =0; $y< imagesy($o); $y++){
          $pixo  = imagecolorat($o, $x, $y);
          $pixi  = imagecolorat($i, $x, $y);
          if (luminanceof($pixi) > luminanceof($pixo)){
            imagesetpixel($o, $x, $y, $pixi);
          } 
       };      
    };
    imagejpeg($o, 'output_z_final.jpg', 100);
}

//================================== main ==================================



try_loading_a_cache_if_exists($i);


//count the pixels to heal
report('starting to count the pixels that need to be healed');

$pixels_to_heal = 0;

$skip_y = array();

for ($y = imagesy($i)-1; $y > -1; $y--){
   $th = 0;
   for ($x = imagesx($i)-1; $x > -1; $x--){       
          $pix = imagecolorat($i, $x, $y);
          if (maxcolororluminance($pix) < $GLOBALS['minlumi']){
             $pixels_to_heal++;
             $th++;
          }         
   }
   if ($th==0){
      $skip_y[] = $y;
      report('nothing to heal in line '.$y.', adding to ignore list');
   }
}

report('counted '.$pixels_to_heal.' pixels to heal, and save in '.$stages_to_save.' stages.<br />');
report(count($skip_y).' lines already being ignored');

$save_stage_at_x_heals = floor($pixels_to_heal / max($stages_to_save, 1))+1;

//iterate and heal

$healed = 0;


$maxx = imagesx($i);
$maxy = imagesy($i);

//$maxx = 300;
//$maxy = 300;
//$iterations = 10;

$st = 0;



$lumi_delta = 255 - $GLOBALS["maxlumi"];


$healed_in_this_round = 0;

$GLOBALS['maxlumi_orig'] = $GLOBALS["maxlumi"];

$skip_y_orig = $skip_y;
$st2 = 0;

$range_sx = array();
for ($sx = 0; $sx < imagesx($i); $sx = $sx + $GLOBALS['max_damage']){
   $range_sx[] = $sx;
};
shuffle($range_sx);
$range_sy = array();
for ($sy = 0; $sy < imagesy($i); $sy = $sy + $GLOBALS['max_damage']){
   $range_sy[] = $sy;
};
shuffle($range_sy);

//for random pairs of areas

$pairs = array();
foreach ($range_sy as $sy){
   foreach ($range_sx as $sx){
      $pairs[] = array($sx, $sy);
   }
};

shuffle($pairs);

for ($main_iteration = 0; $main_iteration < 2; $main_iteration++){
 foreach ($pairs as $pair){
    $sx = $pair[0];
    $sy = $pair[1];
    $GLOBALS['mincontext'] = 5; //start high then fall down
    $GLOBALS["maxlumi"] = $GLOBALS["maxlumi_orig"];
    $startx = $sx;
    $starty = $sy;
    $endy = min(imagesy($i), $starty + $GLOBALS['max_damage']);
    $endx = min(imagesx($i), $startx + $GLOBALS['max_damage']);
    $skip_y = $skip_y_orig;
    $to_heal_in_iteration = 1;//dummy
    $max_iterations = 50; // 10 still leaves holes
    for ($iterations = 0; $iterations < $max_iterations; $iterations++) if ($to_heal_in_iteration > 0){
        $h1 = $healed;   
        report("starting to heal [$startx, $starty, $endx, $endy]. Already healed = ".$healed.' of '.$pixels_to_heal.', '.
               $healed_in_this_round.' in the last round. Mincontext now = '.$GLOBALS['mincontext'].'<br />'); 
        
        //checking lines that are ready    
        $to_heal_in_iteration = 0;
        for ($y =$starty; $y<$endy; $y++) if (!in_array($y, $skip_y)){  
            $to_heal = 0;
            for ($x = $startx; $x<$endx; $x++){
                $pix = imagecolorat($i, $x, $y);
                if (maxcolororluminance($pix) < $GLOBALS['minlumi']){
                    $to_heal++;
                    $to_heal_in_iteration++;
                }            
            }
            if ($to_heal == 0){
               $skip_y[] = $y;
               report('added line '.$y.' to those that are ready and can be ignored<br />');
           }
        };
   
  
        $xrange = range($startx, $endx-1);
        shuffle($xrange);   
        $yrange = range($starty, $endy-1);
        shuffle($yrange);   
   
        $method = $iterations; //egyszer igy, egyszer ugy

        // don't rerun empty lines   
        report('starting to handle pixels');
        if ($method % 2 == 1){
             report('method 1 running<br />');
             foreach($yrange as $y) if (!in_array($y, $skip_y)){
                foreach($xrange as $x){
                   handle_this_pixel($i, $x, $y, $healed, $save_stage_at_x_heals, $st);
                }
             }
        }else{
             report('method 2 running<br />');
             foreach($xrange as $x){
                foreach($yrange as $y) if (!in_array($y, $skip_y)){
                   handle_this_pixel($i, $x, $y, $healed, $save_stage_at_x_heals, $st);
               }
             }
        }
        report('done');   
   
        //checking if the process' tolerance is low enough
        $healed_in_this_round = $healed - $h1;   
        if ($healed_in_this_round < 100){
           if ($GLOBALS['mincontext'] == 3){
               report('few pixels healed, mincontext already small so increasing lumi tolerance<br />');
               $GLOBALS['maxlumi'] = min(255, floor($GLOBALS['maxlumi']+$lumi_delta / 10));
           };          
           if ($GLOBALS['mincontext'] > 3){
              report('few pixels healed so decreased mincontext from '.$GLOBALS['mincontext'].'<br />');
              $GLOBALS['mincontext']--;
           };   
        }
   
        /* if ($iterations > 1){
           ////not doing this with the smaller areas 
           if ($GLOBALS['mincontext'] > 4){
              report('decreased mincontext from '.$GLOBALS['mincontext'].'<br />');
              $GLOBALS['mincontext']--;
           }
        }
        */

        if ($iterations > 3){
           if ($GLOBALS['mincontext'] > 3){
              report('decreased mincontext from '.$GLOBALS['mincontext'].'<br />');
              $GLOBALS['mincontext']--;
           }
        }
    };
    $st2++;
    imagejpeg($i, 'outpot_2_asregion_'.numberaskilo($st2).'.jpg');  
  };//for pairs
};//main_iteration
    
imagejpeg($i, 'output_8_before_lighterthan.jpg', 100);    
    
filter_lighterthanoriginal($i);    

// output a blinking interface to reveal the differences

echo '<img id = "img1" src="input.jpg" width="800" />';
echo '<img id = "img2" src="output_z_final.jpg?'.mt_rand().'" width = "800" style="display:none"/>';

?>
</div>
<script>
setInterval(function (){
   var a = document.getElementById("img1");
   var b = document.getElementById("img2");
   if (a.style.display!="none"){
      a.style.display = "none";
      b.style.display = "";
   }else{
      a.style.display = "";
      b.style.display = "none";
   }
}, 1000);
</script>
Facebooktwitterredditpinterestlinkedinmail