木星観測報告用文字入れスクリプト
2019年シーズンから、撮影した木星の画像を ALPO-Jに報告しています。
個人的には画像の上が南(いわゆる South up)に慣れているので、画像処理や保存は South up派なのですが、報告にあたって、昨今は(特に海外を意識すると)North upが増えているように感じ、迷いながらも North upで報告しています。
報告の際に、画像を North upになるように回転し、撮影日時から体系ごとの中央経度を計算して、画像内に各種撮影条件などとともに入れることになりますが、数が多くなるほど地味に面倒なので、それらを一気に処理するために作った perlスクリプトです。
観測画像のサイズが変動しても同じサイズにリサイズします。
perlモジュールは以下のものを使っています。
- Image::Magick
- File::Basename
文字を入れる位置などはスクリプトに埋め込みで、望遠鏡などのある程度固定的なデータとシーイングなどの画像ごとに変動するデータはテキストファイルから読み込む仕様です。
また、日時は WinJUPOSから吐き出されるファイル名(YYYY-MM-DD_HHMM_s[.*].jpg)から採るようにしていて、テキストファイルのベースネームは画像ファイルと同じであることを前提としています。
個人でしか使わない前提で書いたものですが、誰かの目に留まって便利に使っていただけるようならと思いひっそりと公開します。At your own riskで、ご自由にお使いください。
使い方
- 画像ファイル名確認
- テキストファイルの用意
- 画像ファイルと同じ basenameで、拡張子は '.txt'としたテキストファイルをカレントディレクトリに準備します。ひな型となるファイルを用意しておけば、それをコピーして編集するだけですみます。
- スクリプト実行
ひな型の例
# # annotation text # ### SEEING ?/10 TRANSP ?/5 GAIN 260 SS 5.0 EXP 30 STACK_PCT best 50% DEROTATION 12(6min) ## ======= OBJECT Jupiter TELESCOPE_DIA 150 TELESCOPE_FL 1260 TELESCOPE_TYPE Newtonian TELESCOPE_OPT 2X Barlow lens CAMERA_MAKER ZWO CAMERA_TYPE ASI 462MC FILTER ZWO UV/IR cut filter
コード
#!/usr/local/bin/perl
#
sub usage {
print <<"EOM";
Usage: $0 <--image-from file>
Input image file name is important!!
It should have format as 'YYYY-MM-DD_HHMM_s[.*].jpg'
(default name convention of WinJUPOS)
EOM
exit (0);
}
#
# Created: 2019/05/28 yamane Initial revision.
# Revised: 2019/09/12 yamane Add CM calculation routine.
# Special thanks to M.Sato.
# Revised: 2023/11/10 yamane Enlarge canvas size and adjust text position.
#
use strict;
use Image::Magick;
use File::Basename 'basename';
use Data::Dumper;
# =====================================================
# const
# =====================================================
my $DEBUG = 0;
## my $CANVAS_SIZE_X = 640;
## my $CANVAS_SIZE_Y = 640;
## my $IMAGE_SIZE_X = 800;
## my $IMAGE_SIZE_Y = 800;
my $CANVAS_SIZE_X = 720;
my $CANVAS_SIZE_Y = 800;
my $IMAGE_SIZE_X = 960;
my $IMAGE_SIZE_Y = 960; # should be same as $IMAGE_SIZE_X
## my $FONT_BASE = "DejaVu-Sans-Mono";
my $FONT_BASE = "DejaVu-Sans";
my $FONT_COLOR = "white";
my $TEXTFILE_FMT = "%04d-%02d-%02d-%02d%02d_%1d.txt";
my $OUTFILE_FMT = "obs_%04d-%02d-%02d_%02d-%02d_%1d.jpg";
# =====================================================
# globals
# =====================================================
my $imagefile = "./input_image.jpg"; # default
my $textfile = "./text.txt"; # default
my $outfile = "./output_image.jpg"; # default
my %Config = (
# --- defauls: these should be read from textfile input
'DATE' => '9999/99/99',
'TIME' => '99:99.9',
'CM_I' => 999.9,
'CM_II' => 999.9,
'CM_III' => 333.3,
'SEEING' => '9/5',
'TRANSP' => '9/5',
'GAIN' => 999,
'SS' => 99,
'EXP' => 99,
'STACK_PCT' => 99,
'DEROTATION' => 99,
# --- almost fixed: will be overriden by testfile input
'TELESCOPE_DIA' => 999,
'TELESCOPE_FL' => 9999,
'TELESCOPE_TYPE' => '????????',
'TELESCOPE_OPT' => '2X Barlow',
'CAMERA_MAKER' => '???',
'CAMERA_TYPE' => '????????',
'FILTER' => '??? ????? fter',
); # hasref: read from text file
if ($DEBUG) { print("DEBUG: Config\n"); print(Dumper {%Config}); print("\n"); }
# =====================================================
# main
# =====================================================
while ($#ARGV >= 0) {
if ($ARGV[0] eq '--image-from') {
$imagefile = $ARGV[1]; shift;
print("INFO: imagefile=$imagefile\n");
} else {
print(STDERR "Unknown option $ARGV[0].\n");
usage();
}
shift;
}
# ---------------------------------------------------------
# * get image file name and Date/Time from its name.
# * apply them to output file name
# ---------------------------------------------------------
if (! -f $imagefile) {
print(STDERR "ERROR: $imagefile: no such image file.\n");
usage();
}
my $basename = basename($imagefile);
if ($basename =~ /(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})_(\d).*\.jpg/) {
my ($yy, $mm, $dd, $hh, $mi, $mf) = ($1 + 0, $2 + 0, $3 + 0, $4 + 0, $5 + 0, $6 + 0);
$Config{'DATE'} = sprintf("%04d/%02d/%02d", $yy, $mm, $dd);
$Config{'Time'} = sprintf("%02d:%02d.%d", $hh, $mi, $mf);
$outfile = sprintf($OUTFILE_FMT, $yy, $mm, $dd, $hh, $mi, $mf);
$textfile = sprintf($TEXTFILE_FMT, $yy, $mm, $dd, $hh, $mi, $mf);
print("DEBUG: Config{'DATE'}=$Config{'DATE'} / Config{'Time'}=$Config{'Time'} / outfile='$outfile'\n") if ($DEBUG);
print("INFO: textfile='$textfile'\n");
print("INFO: outfile='$outfile'\n");
## Calculate CM[1..3]
$mi = "$mi.$mf" + 0.0;
my $tz = 0; # for UT
my ($mjd) = MJD($yy, $mm, $dd, $hh, $mi, $tz);
print("DEBUG: yy=$yy / mm=$mm / dd=$dd / hh=$hh / mi=$mi / tz=$tz ==> $mjd\n");
my ($cm1, $cm2, $cm3) = CM($mjd);
$Config{'CM_I'} = sprintf("%-6.1f", $cm1);
$Config{'CM_II'} = sprintf("%-6.1f", $cm2);
$Config{'CM_III'} = sprintf("%-6.1f", $cm3);
print("DEBUG: CM_I=$Config{'CM_I'} / CM_II=$Config{'CM_II'} / CM_III=$Config{'CM_III'}\n");
}
# ---------------------------------------------------------
# read variable data from file
# ---------------------------------------------------------
if (! -r $textfile) {
print(STDERR "ERROR: $textfile: no such text file found.\n");
usage();
}
if (! open(TXT, "$textfile")) {
print(STDERR "ERROR: $textfile: open() failed.\n");
exit (9);
}
my @tmp_ks = keys(%Config);
if ($DEBUG) { print("DEBUG: tmp_ks\n"); print(Dumper @tmp_ks); print("\n"); }
while () {
next if (/^\s*#/);
chomp;
my ($k, $v) = split(/\t+/);
print("DEBUG: read_text(): k=$k / v='$v'\n") if ($DEBUG);
if (grep(/^${k}$/, @tmp_ks)) {
print("DEBUG: regist to config: $k -> '$v'\n") if ($DEBUG);
$Config{$k} = $v;
}
}
close(TXT);
# ---------------------------------------------------------
# execute: read orig image -> resize -> extent
# ---------------------------------------------------------
my $img = Image::Magick->new;
if ($img->Read($imagefile) != 1) {
print(STDERR "ERROR: Read Image failed.\n");
exit (9);
}
if ($img->Rotate(degrees=>180) == 0) {
print(STDERR "ERROR: Rotate failed.\n");
exit (9);
}
if ($img->Resize(width=>${IMAGE_SIZE_X}, height=>${IMAGE_SIZE_Y}) == 0) {
print(STDERR "ERROR: Resize failed.\n");
exit (9);
}
if (${CANVAS_SIZE_X} > ${IMAGE_SIZE_X}) {
my $dx = (${IMAGE_SIZE_X} - ${CANVAS_SIZE_X}) / 2;
my $dy = (${IMAGE_SIZE_Y} - ${CANVAS_SIZE_Y}) / 2;
print("INFO: extent image.");
if ($img->Extent(background=>"black", width=>${CANVAS_SIZE_X}, height=>${CANVAS_SIZE_Y}, x=>$dx, y=>$dy) == 0) {
print(STDERR "ERROR: Extent failed.\n");
exit (9);
}
} elsif (${CANVAS_SIZE_X} < ${IMAGE_SIZE_X}) {
my $dx = (${IMAGE_SIZE_X} - ${CANVAS_SIZE_X}) / 2;
my $dy = (${IMAGE_SIZE_Y} - ${CANVAS_SIZE_Y}) / 2;
print("INFO: crop image.");
if ($img->Crop(width=>${CANVAS_SIZE_X}, height=>${CANVAS_SIZE_Y}, x=>$dx, y=>$dy) == 0) {
print(STDERR "ERROR: Crop failed.\n");
exit (9);
}
}
# ---------------------------------------------------------
# execute: annotation
# ---------------------------------------------------------
my $text = [
# --- Title --- Fiexed
{ "bold"=>1, "pt"=>24, "x"=>180, "y"=> 90, "text"=>"Jupiter" },
{ "bold"=>0, "pt"=>10, "x"=>710, "y"=>104, "text"=>"North Up image" },
# --- Date/Time --- read from textfile
{ "bold"=>0, "pt"=>18, "x"=>220, "y"=>125, "text"=>"$Config{'DATE'}" },
{ "bold"=>0, "pt"=>18, "x"=>330, "y"=>125, "text"=>"$Config{'Time'} UT" },
# --- seeing/transparency --- read from textfile
{ "bold"=>0, "pt"=>14, "x"=>470, "y"=>130, "text"=>"Seeing: $Config{'SEEING'}" },
{ "bold"=>0, "pt"=>14, "x"=>580, "y"=>130, "text"=>"Transp: $Config{'TRANSP'}" },
# --- Central Longitude for each system --- read from textfile
{ "bold"=>0, "pt"=>14, "x"=>220, "y"=>150, "text"=>"CM-I=$Config{'CM_I'}" },
{ "bold"=>0, "pt"=>14, "x"=>400, "y"=>150, "text"=>"CM-II=$Config{'CM_II'}" },
{ "bold"=>0, "pt"=>14, "x"=>580, "y"=>150, "text"=>"CM-III=$Config{'CM_III'}" },
# --- Telescope --- almost fiexed
{ "bold"=>0, "pt"=>14, "x"=>250, "y"=>770, "text"=>"D=$Config{'TELESCOPE_DIA'}mm f.l.=$Config{'TELESCOPE_FL'}mm $Config{'TELESCOPE_TYPE'} $Config{'TELESCOPE_OPT'}" },
# --- Camera --- almost fiexed
{ "bold"=>0, "pt"=>14, "x"=>250, "y"=>790, "text"=>"$Config{'CAMERA_MAKER'} $Config{'CAMERA_TYPE'}" },
{ "bold"=>0, "pt"=>14, "x"=>500, "y"=>790, "text"=>"$Config{'FILTER'}" },
# --- exposure --- read from textfile
{ "bold"=>0, "pt"=>14, "x"=>250, "y"=>810, "text"=>"gain: $Config{'GAIN'}" },
{ "bold"=>0, "pt"=>14, "x"=>380, "y"=>810, "text"=>"ss: $Config{'SS'}ms" },
{ "bold"=>0, "pt"=>14, "x"=>500, "y"=>810, "text"=>"exp: $Config{'EXP'}s" },
# --- image processing --- read from textfile
{ "bold"=>0, "pt"=>14, "x"=>250, "y"=>830, "text"=>"stack: $Config{'STACK_PCT'}" },
{ "bold"=>0, "pt"=>14, "x"=>500, "y"=>830, "text"=>"derotation: $Config{'DEROTATION'}" },
# --- Observer Info. --- Fiexed
{ "bold"=>0, "pt"=>14, "x"=>250, "y"=>850, "text"=>"Satoru Yamane" },
{ "bold"=>0, "pt"=>14, "x"=>380, "y"=>850, "text"=>"(Fujinomiya, Shizuoka, Japan)" },
];
for (my $i = 0; $i < scalar(@$text); $i++) {
my $hashref = @$text[$i];
my $font = ($hashref->{"bold"} == 0) ? $FONT_BASE : "${FONT_BASE}-Bold";
my $pt = $hashref->{"pt"};
my $geom = sprintf("+%d+%d", $hashref->{"x"}, $hashref->{"y"});
my $text = $hashref->{"text"};
print("DEBUG: $i: font=$font / pt=$pt / geom=$geom / text=$text\n") if ($DEBUG);
$img->Annotate(text=>$text, geometry=>$geom,
font=>$font, fill=>$FONT_COLOR,
gravity=>'northwest', pointsize=>$pt);
}
# ---------------------------------------------------------
# execute: write
# ---------------------------------------------------------
if ($img->Write($outfile) == 0) {
print(STDERR "ERROR: $outfile: write failed.\n");
exit (9);
}
undef $img;
print("INFO: completed.\n");
exit (0);
### ========================================================
### Calculate CM
###
### Special thanks to M.Sato
###
### $version = "jupcmcal.cg-v1.1";
### ========================================================
# -----------------------------------------------
# 日数計算サブルーチン
# -----------------------------------------------
sub MJD {
my ($sy, $sm, $sd, $sh, $smin, $stz) = @_;
my ($mjd);
if ($sm > 2) {
$sy = $sy - 1900;
$sm = $sm;
} else {
$sy = $sy - 1901;
$sm = $sm + 12;
}
$mjd = int($sy * 365.25) + int(30.6 * $sm + 0.6) + $sd - 34 + $stz + $sh / 24 + $smin / 1440 + 0.5;
($mjd) ;
}
# -----------------------------------------------
# CM計算
# -----------------------------------------------
sub CM {
my ($mjd) = shift;
my ($cm1, $cm2, $cm3) = (); # return values
my $RAD = 0.0174532925199433;
my $IR = (134.630 + $mjd * 0.00111587) * $RAD;
my $JR = (358.476 + $mjd * 0.9856) * $RAD;
my $KR = (225.328 + $mjd * 0.083085 + 0.33 * sin($IR)) * $RAD;
my $L = 221.650 + $mjd * 0.902518 - 0.33 * sin($IR);
my $M = 1.916 * sin($JR) + 0.02 * sin(2 * $JR);
my $N = 5.552 * sin($KR) + 0.167 * sin(2 * $KR);
my $OR = ($L + $M - $N) * $RAD;
my $P = 1.0 - 0.0167 * cos($JR) - 0.00014 * cos(2 * $JR);
my $Q = 5.21 - 0.252 * cos($KR) - 0.0061 * cos(2 * $KR);
my $R = sqrt($Q * $Q + $P * $P - 2 * $Q * $P * cos($OR));
my $SSI = $P * sin($OR) / $R;
my $SCO = sqrt(1 - $SSI * $SSI);
my $SR = atan2($SSI , $SCO);
my $S = $SR / $RAD;
my $sk = ($S < 0) ? 1 : -1;
my $T = 877.8169088 * ($mjd - $R / 173.0) + $S - $N + 269.10 + 57.3 * $sk * (sin($SR * 0.5)) ** 2;
my $U = 870.1869088 * ($mjd - $R / 173.0) + $S - $N + 291.04 + 57.3 * $sk * (sin($SR * 0.5)) ** 2;
my $V = 870.4529088 * ($mjd - $R / 173.0) + $S - $N + 177.04 + 57.3 * $sk * (sin($SR * 0.5)) ** 2;
$cm1 = $T - 360 * int($T / 360.0);
$cm2 = $U - 360 * int($U / 360.0);
$cm3 = $V - 360 * int($V / 360.0);
($cm1, $cm2, $cm3) ;
}