fukasis 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright © 2026 Tsuyoshi Kobayashi(legrs4073)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,109 @@
1
- # Temporary Holding Version
1
+ # fukasis (コマンドライン版)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ FUKASIS-app で撮影したデータを PC で処理するコマンドラインツールです。
4
+ アプリの **dark / calibration / csv / view** の 4 画面に相当するコマンドがあります。
5
+ まとめて処理したいときや、スクリプトから呼びたいときに使います。画像を見ながら作業するなら [web 版](../web/README.md) が向いています。
6
+
7
+ 計算はアプリ (共通コア `core/`) と同じ結果になるようにしてあります。
8
+
9
+ ## ビルド
10
+
11
+ [Rust](https://www.rust-lang.org/ja/tools/install) が必要です。外部クレートには依存していません。
12
+
13
+ ```bash
14
+ cd cli
15
+ cargo build --release
16
+ ```
17
+
18
+ `cli/target/release/fukasis` (Windows では `fukasis.exe`) ができます。
19
+ `cargo install --path cli` (リポジトリの直下で実行) とすると、`fukasis` コマンドとしてインストールされます。
20
+ GitHub Actions の「PC tools」が動いているリポジトリでは、その実行結果からビルド済みの実行ファイル (Linux / macOS / Windows) も取得できます。
21
+
22
+ ## 使い方
23
+
24
+ スマートフォンの `Internal_Storage/Documents/FUKASIS-app/` を PC にコピーして使います。
25
+
26
+ ### dark: ダーク減算
27
+
28
+ ```bash
29
+ fukasis dark imgs/alphaOri_260314_1/stacked.tif imgs/dark_20260314/stacked.tif
30
+ ```
31
+
32
+ ライトフレームと同じフォルダに `darked.tif` を書きます。`-o` で出力先を変えられます。
33
+
34
+ ### info: 画像の情報
35
+
36
+ ```bash
37
+ fukasis info imgs/fluorescent_260314_1/stacked.tif
38
+ ```
39
+
40
+ 画像の大きさ、0次光の位置の推定、輝線の候補 (明るい順) を表示します。
41
+ 次の `calib` に渡す位置を調べるのに使えます。どちらも推定なので、おかしければ web 版で画像を見て確かめてください。
42
+
43
+ ### calib: 波長校正データを作る
44
+
45
+ ```bash
46
+ fukasis calib --fol 3590 --line 1958=435.8 --line 1604=546.1 --line 1464=588.0 --line 1380=611.6 -o calibdata/fluorescent.csv
47
+ ```
48
+
49
+ `--fol` は 0次光の位置、`--line` は「輝線の位置 = 波長 (nm)」です。位置はどちらも画像上の x 座標 (px) です。
50
+ 校正点は 4 本以上を推奨します。4 本ならその 4 点を通る 3 次式、5 本以上なら 3 次の最小二乗で当てはめます。
51
+ 当てはめとの差と、出力される波長の範囲が表示されます。`-o` を省くと校正データを標準出力に書きます。
52
+
53
+ 既存の校正データを確かめるだけなら:
54
+
55
+ ```bash
56
+ fukasis calib --check calibdata/fluorescent.csv --fol 3590
57
+ ```
58
+
59
+ ### csv: スペクトルを出力
60
+
61
+ ```bash
62
+ fukasis csv --image imgs/alphaOri_260314_1/darked.tif \
63
+ --calib calibdata/fluorescent.csv \
64
+ --sensitivity sensit_distr.csv \
65
+ --metadata imgs/alphaOri_260314_1/metadata.csv \
66
+ -o spectrum/alphaOri_260314_1.csv
67
+ ```
68
+
69
+ - `--fol` を省くと 0次光の位置を自動で推定します (推定した値は表示されます)。ずれている場合は `--fol <x>` で指定してください。
70
+ - `--metadata` は任意です。省くと 1 行目には画像のファイル名が入ります。
71
+ - `-o` を省くと標準出力に書きます。
72
+ - カラーフィルタ配列は、metadata に記録されていればそれを使います (無ければ GBRG)。`--cfa RGGB` のように指定もできます。
73
+ - スペクトルを読む帯は、既定では画像の中央の幅 80 px です。機種によって違う場合は `--band-width <px>` と `--band-center <0〜1>` で変えられます。
74
+ - 出力する範囲は校正データから波長 (400〜700 nm) で決まります。出力できる点が無いときは、理由を表示して終了します。
75
+
76
+ ### graph: グラフを SVG に出力
77
+
78
+ ```bash
79
+ fukasis graph spectrum/alphaOri_260314_1.csv spectrum/betaOri_260314_1.csv -o compare.svg
80
+ ```
81
+
82
+ 8 本まで重ねて描けます。既定では明らかな異常値 (波長が折り返している部分・負の強度・孤立したスパイク) を除いて描きます。
83
+ `--keep-outliers` を付けると除かずに描きます。`--title` で見出しを変えられます。
84
+
85
+ ## 対応している画像
86
+
87
+ TIFF は、アプリが書き出す形式 (無圧縮・1 チャンネル・ストリップ形式。32 bit float のほか 8 / 16 / 32 bit 整数、64 bit float) に対応しています。
88
+ 圧縮された TIFF やタイル形式には対応していません。
89
+
90
+ ## テスト
91
+
92
+ ```bash
93
+ cargo test
94
+ ```
95
+
96
+ アプリの C++ をそのまま動かして作った正解データ (`testdata/`) と出力が一致することを確かめています。
97
+
98
+ ## リリース
99
+
100
+ GitHub Releases・crates.io (`fukasis`)・npm (`fukasis`) で配布できる形にしてあります。手順は [docs/releasing.md](../docs/releasing.md) にあります。
101
+ 公開後は、次のどれでも入れられます。
102
+
103
+ ```bash
104
+ cargo install fukasis
105
+ ```
106
+
107
+ ```bash
108
+ npm install -g fukasis
109
+ ```
package/bin/fukasis.js ADDED
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ // npm で配る fukasis の起動役. optionalDependencies で一緒に入る機種別パッケージ
4
+ // (@fksgeo/fukasis-bin-*) から実行ファイルを探して起動する.
5
+ // esbuild や swc, Biome と同じやり方.
6
+ 'use strict';
7
+
8
+ const { spawnSync } = require('child_process');
9
+ const fs = require('fs');
10
+ const os = require('os');
11
+ const path = require('path');
12
+
13
+ const s = (platform) => `@fksgeo/fukasis-bin-${platform}`;
14
+
15
+ // pkg は既定 (Linux では glibc 版), muslPkg は musl 版 (Alpine など).
16
+ // 32 bit ARM の v6Pkg / v6MuslPkg は ARMv6 版 (Raspberry Pi 1 / Zero).
17
+ // os / cpu だけでは ARMv6 と ARMv7, glibc と musl を区別できず複数入ることがあるので, ここで選ぶ.
18
+ const PLATFORMS = {
19
+ 'darwin arm64': { pkg: s('darwin-arm64') },
20
+ 'darwin x64': { pkg: s('darwin-x64') },
21
+ 'win32 x64': { pkg: s('win32-x64') },
22
+ 'win32 arm64': { pkg: s('win32-arm64') },
23
+ 'win32 ia32': { pkg: s('win32-ia32') },
24
+ 'linux x64': { pkg: s('linux-x64'), muslPkg: s('linux-x64-musl') },
25
+ 'linux arm64': { pkg: s('linux-arm64'), muslPkg: s('linux-arm64-musl') },
26
+ 'linux ia32': { pkg: s('linux-ia32'), muslPkg: s('linux-ia32-musl') },
27
+ 'linux arm': {
28
+ pkg: s('linux-arm'),
29
+ muslPkg: s('linux-arm-musl'),
30
+ v6Pkg: s('linux-armv6'),
31
+ v6MuslPkg: s('linux-armv6-musl'),
32
+ },
33
+ 'linux riscv64': { pkg: s('linux-riscv64'), muslPkg: s('linux-riscv64-musl') },
34
+ // 配っているのはリトルエンディアン (ppc64le) 版だけ
35
+ 'linux ppc64': { pkg: s('linux-ppc64'), littleEndianOnly: true },
36
+ 'linux s390x': { pkg: s('linux-s390x') },
37
+ 'linux loong64': { pkg: s('linux-loong64') },
38
+ 'android arm64': { pkg: s('android-arm64') },
39
+ 'android arm': { pkg: s('android-arm') },
40
+ 'android x64': { pkg: s('android-x64') },
41
+ 'android ia32': { pkg: s('android-ia32') },
42
+ 'freebsd x64': { pkg: s('freebsd-x64') },
43
+ 'freebsd ia32': { pkg: s('freebsd-ia32') },
44
+ 'netbsd x64': { pkg: s('netbsd-x64') },
45
+ 'sunos x64': { pkg: s('sunos-x64') },
46
+ };
47
+
48
+ // Node の process.report には, glibc 版の Node なら glibc のバージョンが入っている (musl 版には無い).
49
+ // process.report が無い古い Node では ldd の中身で判断する.
50
+ function isMusl() {
51
+ if (process.platform !== 'linux') return false;
52
+ if (!process.report || typeof process.report.getReport !== 'function') {
53
+ try {
54
+ return fs.readFileSync('/usr/bin/ldd', 'utf8').includes('musl');
55
+ } catch (e) {
56
+ return false;
57
+ }
58
+ }
59
+ const { glibcVersionRuntime } = process.report.getReport().header;
60
+ return !glibcVersionRuntime;
61
+ }
62
+
63
+ // ARMv6 の機械では ARMv7 版は動かない. Node はビルドされた ARM のバージョンを覚えている
64
+ function isArmV6() {
65
+ return process.arch === 'arm' && String(process.config.variables.arm_version) === '6';
66
+ }
67
+
68
+ function resolveFromPkg(pkg, exe) {
69
+ try {
70
+ return require.resolve(`${pkg}/bin/${exe}`);
71
+ } catch (e) {
72
+ // node_modules/fukasis/bin/ から見た node_modules/<pkg>/bin/
73
+ const local = path.join(__dirname, '..', '..', ...pkg.split('/'), 'bin', exe);
74
+ return fs.existsSync(local) ? local : null;
75
+ }
76
+ }
77
+
78
+ // 試すパッケージを, 合うものから順に並べる.
79
+ // musl 版は静的リンクなので glibc の機械でも動く. glibc 版が入っていないときの控えにする
80
+ function candidatesFor(entry, onMusl, armV6) {
81
+ const glibc = armV6 ? [entry.v6Pkg] : [entry.pkg, entry.v6Pkg];
82
+ const musl = armV6 ? [entry.v6MuslPkg] : [entry.muslPkg, entry.v6MuslPkg];
83
+ return (onMusl ? [...musl, ...glibc] : [...glibc, ...musl]).filter(Boolean);
84
+ }
85
+
86
+ // 起動する実行ファイルを { path, pkg, muslFallback } で返す. 見つからなければ { error }.
87
+ // muslFallback は「musl の機械なのに glibc 版しか見つからなかった」とき true.
88
+ // その glibc 版はたいてい起動できず, ただの ENOENT になるので, 呼び出し側で説明を足す.
89
+ function findBinary({
90
+ platform = process.platform,
91
+ arch = process.arch,
92
+ endianness = os.endianness(),
93
+ musl = isMusl,
94
+ armV6 = isArmV6,
95
+ resolve = resolveFromPkg,
96
+ } = {}) {
97
+ const key = `${platform} ${arch}`;
98
+ const entry = PLATFORMS[key];
99
+ if (!entry || (entry.littleEndianOnly && endianness !== 'LE')) {
100
+ return {
101
+ error:
102
+ `fukasis: this platform (${key}) has no prebuilt binary on npm.\n` +
103
+ 'See https://github.com/igarinpiano/fukasis/releases for more builds, or build from source: cargo install fukasis',
104
+ };
105
+ }
106
+ const exe = platform === 'win32' ? 'fukasis.exe' : 'fukasis';
107
+ const onMusl = Boolean(entry.muslPkg) && musl();
108
+ const candidates = candidatesFor(entry, onMusl, arch === 'arm' && armV6());
109
+ const muslPkgs = [entry.muslPkg, entry.v6MuslPkg].filter(Boolean);
110
+ for (const pkg of candidates) {
111
+ const resolved = resolve(pkg, exe);
112
+ if (resolved) return { path: resolved, pkg, muslFallback: onMusl && !muslPkgs.includes(pkg) };
113
+ }
114
+ return {
115
+ error:
116
+ `fukasis: could not find the binary package (${candidates.join(' / ')}).\n` +
117
+ 'Try reinstalling: npm install -g fukasis (without --omit=optional / --no-optional).',
118
+ };
119
+ }
120
+
121
+ function launchErrorMessage(found, error, arch = process.arch) {
122
+ let msg = `fukasis: failed to launch: ${error.message}`;
123
+ if (found.muslFallback && error.code === 'ENOENT') {
124
+ const muslPkg = PLATFORMS[`linux ${arch}`] && PLATFORMS[`linux ${arch}`].muslPkg;
125
+ msg +=
126
+ `\nfukasis: this looks like a musl system (Alpine, etc.), but only the glibc build (${found.pkg}) is installed,` +
127
+ ' and it cannot run without glibc.' +
128
+ `\nInstall the musl build: npm install -g ${muslPkg}`;
129
+ }
130
+ return msg;
131
+ }
132
+
133
+ function main() {
134
+ const found = findBinary();
135
+ if (found.error) {
136
+ console.error(found.error);
137
+ process.exit(1);
138
+ }
139
+ let result = spawnSync(found.path, process.argv.slice(2), { stdio: 'inherit' });
140
+ // 実行権限が落ちていたら付け直してもう一度
141
+ if (result.error && result.error.code === 'EACCES') {
142
+ try {
143
+ fs.chmodSync(found.path, 0o755);
144
+ result = spawnSync(found.path, process.argv.slice(2), { stdio: 'inherit' });
145
+ } catch (e) {
146
+ // 下でもとのエラーを表示する
147
+ }
148
+ }
149
+ if (result.error) {
150
+ console.error(launchErrorMessage(found, result.error));
151
+ process.exit(1);
152
+ }
153
+ process.exit(result.status === null ? 1 : result.status);
154
+ }
155
+
156
+ if (require.main === module) {
157
+ main();
158
+ }
159
+
160
+ module.exports = { PLATFORMS, findBinary, launchErrorMessage };
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "fukasis",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Process spectra captured with FUKASIS-app on a PC: dark subtraction, wavelength calibration, CSV export and SVG graphs",
5
+ "keywords": [
6
+ "spectroscopy",
7
+ "astronomy",
8
+ "spectrum",
9
+ "calibration",
10
+ "tiff",
11
+ "cli"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/igarinpiano/fukasis.git"
16
+ },
17
+ "homepage": "https://github.com/igarinpiano/fukasis/tree/master/cli",
18
+ "bugs": "https://github.com/igarinpiano/fukasis/issues",
19
+ "license": "MIT",
20
+ "bin": {
21
+ "fukasis": "bin/fukasis.js"
22
+ },
23
+ "files": [
24
+ "bin/",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "engines": {
29
+ "node": ">=16"
30
+ },
31
+ "optionalDependencies": {
32
+ "@fksgeo/fukasis-bin-darwin-arm64": "0.1.0",
33
+ "@fksgeo/fukasis-bin-darwin-x64": "0.1.0",
34
+ "@fksgeo/fukasis-bin-win32-x64": "0.1.0",
35
+ "@fksgeo/fukasis-bin-win32-arm64": "0.1.0",
36
+ "@fksgeo/fukasis-bin-win32-ia32": "0.1.0",
37
+ "@fksgeo/fukasis-bin-linux-x64": "0.1.0",
38
+ "@fksgeo/fukasis-bin-linux-x64-musl": "0.1.0",
39
+ "@fksgeo/fukasis-bin-linux-arm64": "0.1.0",
40
+ "@fksgeo/fukasis-bin-linux-arm64-musl": "0.1.0",
41
+ "@fksgeo/fukasis-bin-linux-ia32": "0.1.0",
42
+ "@fksgeo/fukasis-bin-linux-ia32-musl": "0.1.0",
43
+ "@fksgeo/fukasis-bin-linux-arm": "0.1.0",
44
+ "@fksgeo/fukasis-bin-linux-arm-musl": "0.1.0",
45
+ "@fksgeo/fukasis-bin-linux-armv6": "0.1.0",
46
+ "@fksgeo/fukasis-bin-linux-armv6-musl": "0.1.0",
47
+ "@fksgeo/fukasis-bin-linux-riscv64": "0.1.0",
48
+ "@fksgeo/fukasis-bin-linux-riscv64-musl": "0.1.0",
49
+ "@fksgeo/fukasis-bin-linux-ppc64": "0.1.0",
50
+ "@fksgeo/fukasis-bin-linux-s390x": "0.1.0",
51
+ "@fksgeo/fukasis-bin-linux-loong64": "0.1.0",
52
+ "@fksgeo/fukasis-bin-android-arm64": "0.1.0",
53
+ "@fksgeo/fukasis-bin-android-arm": "0.1.0",
54
+ "@fksgeo/fukasis-bin-android-x64": "0.1.0",
55
+ "@fksgeo/fukasis-bin-android-ia32": "0.1.0",
56
+ "@fksgeo/fukasis-bin-freebsd-x64": "0.1.0",
57
+ "@fksgeo/fukasis-bin-freebsd-ia32": "0.1.0",
58
+ "@fksgeo/fukasis-bin-netbsd-x64": "0.1.0",
59
+ "@fksgeo/fukasis-bin-sunos-x64": "0.1.0"
60
+ }
61
+ }