Astrocamp

Dark Skies 2016 Astrocamp, Rotunda Pass

Amateur astronomers from Máramarossziget/Sighetu Marmației organized the Dark Skies 2016 Astronomy Camp at the Rotunda Pass. The area is one of the last remaining dark spots in the EU, so dark that the clouds appear to be dark brown/black.

The camp being meant mainly for visual observers, had no electricity, just a generator run in the evening, during the dinner to recharge the gadgets. It is the owner’s fault we had no running water at all, an issue I would have imagined for the savanna/desert in Namibia, not the Romanian countryside with a long estabilished cabin with as much water that my jacket got wet in a clear night. So much for the level of civilisation and decency.

The weather turned out to be less than favorable with humidity almost always >90% when the sky was sort of clear, and fog, clouds and rain otherwise. We had about 18 clear night hours with most being lit by the Moon during the whole week.

The cabin at the Rotunda Pass in clear weather.

The cabin at the Rotunda Pass in clear weather.

The yard during most of the camp

The yard during most of the camp

The road to cellphone coverage. There was no service at the camp site.

The road to cellphone coverage. There was no service at the camp site.

The instruments were two small 10 cm Maksutov-Cassegrains, three Dobsonians (a 30, a 25 and a 15 cm reflector, the 15cm called MINTAKA, being hand made, having the mirror polished by István Mátis – Mátis István’s Newton Telescope AKA = Mintaka) and my photo equipment: the modded Canon 1100D and the 200 mm lens on the HEQ5. On the software part, I used my polaris javascript modified for offline use – no cell coverage – and another javascript we developed with István to help him count and classify the meteors.

Some improvisation: the heater issue

Having on my mind the limited access to electricity, I forgot the lens heater I made a while back – big mistake. Fortunately I found some 12V light bulbs at the cabin – given that more than 90% of the energy is radiated as heat, the bulbs are effectively heater rezistors that also emit some waste light. The rest of the job was to figure out a way to goldilock one or more bulbs to my photo gear: don’t illuminate my photo and be just warm enough: not too hot, not too cold. And don’t use more energy than I have, namely two lead batteries, 7.2 Ah each (one for the mount, one spare) with charging times well above the availability of the generator (2-3 hours per day).

I used a small bulb with parts from a pet water bottle keeping it away from the lens, in a pocket of air wich I hoped would give a more distributed heating than just taping the bulb to the lens body.  Since the duct tape was black and absorbed heat (roughly 3/4 of the radiated heat being pointed away from the target) I surrounded the bulb’s air pocket with tin foil to reflect the heat to the target. With the tin foil I felt almost no waste heat on the outside. I also added an – advert alert :P – H&M jumper – with lots of little air pockets – as an outside insulator. This is what happens to trademarks when one views them as a composite of raw materials. Taking a look at the whole thing and the sheer amount of duct tape, although in theory I knew it should work, I was surprised the heater actually worked damn well. It didn’t noticeably heat the lens but kept the fog away even when the flashlight revealed droplets of water storming in the air and objects sometimes cast visible shadows onto the fog.

My battery with a bulb found in the cabin

My battery with a bulb found in the cabin

The bulb I decided to use, also from the cabin. Note the encasement from the pet bottle and the tin foil I used to create a warm pocket of air not to overheat some parts of the lens and leave the others cold.

The bulb I decided to use, also from the cabin. Note the encasement from the pet bottle and the tin foil I used to create a warm pocket of air not to overheat some parts of the lens and leave the others cold.

The pocket in tin foil.

The pocket in tin foil.

The whole thing in duct tape.

The whole thing in duct tape.

The gear with the odd looking cables.

The gear with the odd looking cables.

And the jumper also being helpful.

And the jumper also being helpful.

I had twoo targets, the main being the Heart and Soul in Cassiopeia, the secondary the sadr region around gamma Cygni. I also imaged the Coathanger when the sky allowed nothing else.

The Heart and Soul nebulae

My plan was to acquire hours of light on the Heart and Soul, but the sky thought otherwise. I squeezed out about 8 fair exposures, 4 being with good and 4 with decent transparency (ie less contrast). So the instruments: Canon 1100D mod, 8×3 minutes, ISO 1600 at 200mm F/4 on the HEQ5 mount. For some reason there is a black ring around my bright stars (an image artifact), an issue I addressed with some programming this time (bottom of the post). Also, I have no program to shrink the stars. So I edited out the higlights (stars), feeding the picture with the holes to my code, and then took its output and put it back as a “lighter than” layer. It fixed the dark ring issue with the mostly correct colors.

A raw with at most fair transparency

A raw with at most fair transparency

A raw with good transparency

A raw with good transparency

The stack with some curves

The stack with some curves

Stage: flattened background, emphasized nebulae, hunting for some blue light

Stage: flattened background, emphasized nebulae, hunting for some blue light

Highlights removed and replaced by blacks.

Highlights removed and replaced by blacks.

Filling the blanks with the context.

Filling the blanks with the context.

Blinking gif to show the delta

Blinking gif to show the delta

Heart and Soul nebulae. Final image: 8×3 minutes

Heart and Soul nebulae. Final image: 8×3 minutes

 

The gamma Cygni Region

During the first, somewhat clear night I also imaged the gamma Cygni region. 21×150 sec, ISO 1600 F/4 exposures turned out to be usable. I used an improved version of the “healing” code.

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.

raw

raw

stack with some curves

stack with some curves

artifacts around the stars

artifacts around the stars

prepping to heal

prepping to heal

the healed version – the remaining “blacks” are in fact shades of grey

the healed version – the remaining “blacks” are in fact shades of grey

Stars put back

Stars put back

The gamma Cygni Region, final picture

The gamma Cygni Region, final picture

 

The Coathanger

This was a target of a sky that allowed nothing else. The setup, as before, Canon 1100D mod 29×60 sec, ISO 1600, 200mm F/4, HEQ5.

raw

raw

brute stack of DSS, very creamy

brute stack of DSS, very creamy

recovered the sharpness and the stars

recovered the sharpness and the stars

The Coathanger (final image), Canon 1100D mod 29×60 sec, ISO 1600, 200mm F/4, HEQ5

The Coathanger (final image), Canon 1100D mod 29×60 sec, ISO 1600, 200mm F/4, HEQ5

 

The code to fill the blanks

<!-- 
script to fill the blanks of input.jpg 
by interpolating 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'] = 150;

$stages_to_save = 25;

set_time_limit(60000);//majdnem 24 ora :P



//========================================================================
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_premature.jpg", 95);
               die();
            }
            if (heal_pixel_if_possible($i, $x, $y)){
               $healed++;
               if ($healed % $save_stage_at_x_heals == 0){
                   imagejpeg($i, 'outpot_'.numberaskilo($st).'.jpg', 95);
                   $st++;
               }
            };
         };
//========================================================================


if (!file_exists('outpit.jpg')){
   report('image input loaded, no cache<br />');
   report('starting some cosmetics: removing antialiased pixels, 1 width<br />');
    //do some optimization by caching
    
   // do some cleanup: the edge of the black areas tend to be antialiased
   for ($x = 0; $x < imagesx($i); $x++){
      for ($y = 0; $y < imagesy($i); $y++){
          $pix = imagecolorat($i, $x, $y);
          if (maxcolororluminance($pix) < $GLOBALS['minlumi']){
             imagesetpixel_($i, $x-1, $y-1, 0);
             imagesetpixel_($i, $x-1, $y-0, 0);
             imagesetpixel_($i, $x-0, $y-1, 0);
          };
      }
   }
   for ($x = imagesx($i)-1; $x > -1; $x--){
      for ($y = imagesy($i)-1; $y > -1; $y--){
          $pix = imagecolorat($i, $x, $y);
          if (maxcolororluminance($pix) < $GLOBALS['minlumi']){
             imagesetpixel_($i, $x+1, $y+1, 0);
             imagesetpixel_($i, $x+1, $y+0, 0);
             imagesetpixel_($i, $x+0, $y+1, 0);
          };
      }
   }
   //save the cleaned up version
   report('saving the carved version<br />');
   imagejpeg($i, "outpit.jpg", 95);
}else{
   report('image outpIt loaded from cache<br />');
   imagedestroy($i);
   $i = imagecreatefromstring(file_get_contents('outpit.jpg'));
}


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

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
    for ($iterations = 0; $iterations < $GLOBALS["max_damage"]*2; $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_asregion_'.numberaskilo($st2).'.jpg');  
};
    
    
    
//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($i, 'output.jpg', 95);




// output a blinking interface to reveal the differences

echo '<img id = "img1" src="input.jpg" width="800" />';
echo '<img id = "img2" src="output.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>

erwgyrty

facebooktwittergoogle_plusredditpinterestlinkedinmail