Spaces:
Sleeping
Sleeping
| # Analyze API Spec (λ¨μΌ μ€νν / v1) | |
| μ΄ λ¬Έμλ **λ¨μΌ μ§μ€ 곡κΈμ(SSOT)**μ΄λ€. ꡬνΒ·ν μ€νΈΒ·νλ‘ νΈ κ³μ½μ μ¬κΈ° μ μν μ€ν€λ§μ μλ―Έλ₯Ό λ°λ₯Έλ€. | |
| - **μλν¬μΈνΈ:** `POST /analyze` | |
| - **μΆλ ₯:** λͺ¨λ°μΌ/μΉμμ λ°λ‘ μΈ μ μλ **JSON ν λ©μ΄λ¦¬**(MusicXML λ±μ HTTP μλ΅μ ν¬ν¨νμ§ μμ; μλ² λ΄λΆ μ μ© κ°λ₯). | |
| - **μμ μλ΅ λ¬Άμ:** `docs/analyze-response-format-examples.json` | |
| --- | |
| ## 1) μ ν λ²μ (μ¬ν λͺ¨λ) | |
| ### 1.1 μ λ ₯ μ΄λ―Έμ§μ λν κ°μ | |
| 1. **ν μ΄λ―Έμ§ = μ€μ (staff) μ νν ν μ€**λ§ μλ€κ³ κ°μ νλ€. | |
| - 볡μ μ€(μ: νΌμλ Έ λ보ν), ν©μ°½ λ€μ€ μ€νν λ±μ **μ΄ μ€νμ λ²μ λ°**μ΄λ€. | |
| 2. μ¬μ§μ **μΈμ μ 보λ₯Ό μΉ΄λ©λΌλ‘ μ°μ λ€ ν¬λ‘**ν κ²μΌ μ μλ€. | |
| - μ’ μ΄ νμ΄μ§, κΈ°μΈκΈ°, μ‘°λͺ , μκΈμ¨ λμ λ±μ΄ μμ μ μμΌλ©°, μλ²λ **μ μ²λ¦¬(OpenCV λ±) + νμ μ λ₯λ¬λ κΈ°λ° OMR**μΌλ‘ μ΄μ λμνλ€(ꡬν λν μΌμ μ½λΒ·λ³λ λ¬Έμ; μ¬κΈ°μλ **μ μΆλ ₯ κ³μ½**λ§ κ³ μ νλ€). | |
| ### 1.2 μν₯ λ΄μ©μ λν κ°μ | |
| - **λ¨μ μ¨(λ¨μ)** λλ **νμ(λμμ μΈλ¦¬λ μ, μ΅λ 2μ±λΆ)**μ΄ μ¬ μ μλ€. | |
| - νμμΈ κ²½μ°, **κ²°κ³Ό λ©λ‘λ μνμ€λ νμ βκ°μ₯ λμ μνβλ§** μ¬μ©νλ€(2μ±λΆλ₯Ό ν μμΌλ‘ μ€μ΄λ κ·μΉ). | |
| - **μμ리ν(clef)** μ **μ‘°ν(key signature)** λ μ΄λ―Έμ§ 맨 μμμ μλ € λκ°μ μ μλ€. μ΄ λ μ 보λ **νμ ν΄λΌμ΄μΈνΈκ° μμ²κ³Ό ν¨κ» μ 곡**νλ€(μλ `score_context`). | |
| ### 1.3 μ¬λ¬ μ₯ μ΄λ―Έμ§ | |
| - ν μμ²μ **μ¬λ¬ μ΄λ―Έμ§**λ₯Ό λ³΄λΌ μ μλ€. νμΌ **μ λ‘λ μμ = μ 보 μ½λ μμ(μΌμͺ½βμ€λ₯Έμͺ½ μ΄μ΄μ§)**μ΄λ€. | |
| - μλ²λ **μ΄λ―Έμ§λ§λ€ λ 립μ μΌλ‘ μΈμ νμ΄νλΌμΈ**μ λ릴 μ μλ€. | |
| - μλ΅μ **λ©λ‘λ νμλΌμΈμ νλ**λ‘ λ³ν©νλ€. μ¦, `onset_div` / `duration_div` λ **λ§μΉ ν μ₯μ§λ¦¬ μ λ ₯μ΄μλ κ²μ²λΌ** μ΄μ΄μ§ λ¨μΌ μνμ€λ€. | |
| --- | |
| ## 2) μμ² (Request) | |
| ### 2.1 μ μ‘ νμ | |
| - **Content-Type:** `multipart/form-data` | |
| ### 2.2 νλ | |
| | νλ | νμ | μ€λͺ | | |
| |------|------|------| | |
| | `score_context` | **μ** | JSON λ¬Έμμ΄. μλμ΄ ν΄μμ νμν **clefΒ·μ‘°ν**(λ° μ ν νλ). νμμ Β§2.3. | | |
| | `file` | μ‘°κ±΄λΆ | λ¨μΌ μ΄λ―Έμ§ (`jpg`, `jpeg`, `png`, `webp`). | | |
| | `files` | μ‘°κ±΄λΆ | λμΌ ν€ λ°λ³΅μΌλ‘ μ¬λ¬ μ΄λ―Έμ§. μ: `-F "files=@a.png" -F "files=@b.png"` | | |
| | `options` | μλμ€ | JSON λ¬Έμμ΄. νμμ Β§2.4. | | |
| κ·μΉ: | |
| - `file` κ³Ό `files` μ€ **νλλ λ°λμ** μ‘΄μ¬ν΄μΌ νλ€. | |
| - λ λ€ μμΌλ©΄ **`files` μ°μ **νλ€. | |
| - `score_context` κ° μκ±°λ JSON νμ± μ€ν¨ μ **400**. | |
| ### 2.3 `score_context` (JSON κ°μ²΄) | |
| **νμ:** | |
| | ν€ | νμ | μ€λͺ | | |
| |----|------|------| | |
| | `clef` | `string` | μμ리ν. νμ© κ°: `treble`, `bass`. (μΆν `alto`, `tenor` λ± νμ₯ κ°λ₯νλ v1 ꡬνμ treble/bass μ°μ .) | | |
| | `key_signature` | `object` | μ‘°ν. | | |
| | `key_signature.fifths` | `integer` | μ₯μ‘° κΈ°μ€ **μ‘°νμ μ΅/νλ« κ°μ**. λ²μ **-6 ~ 6**. μμ=νλ«, μμ=μ΅, `0`=λ€μ₯μ‘°. | | |
| **μ ν (v1μμ κΆμ₯Β·κΈ°λ³Έκ° μμ):** | |
| | ν€ | νμ | κΈ°λ³Έ | μ€λͺ | | |
| |----|------|------|------| | |
| | `time_signature` | `string` | `"4/4"` | `"<beats>/<beat-type>"` νμ. μ: `"3/4"`, `"6/8"`. | | |
| | `tempo_bpm_reference` | `number \| null` | `null` | UIΒ·ν΄λ¦ νΈλμ© μ°Έκ³ ν ν¬. λ―Έμ§μ μ νλ‘ νΈκ° μ체 κΈ°λ³Έκ° μ¬μ©. | | |
| | `divisions` | `integer` | `4` | νεει³η¬¦(4λΆμν)λ₯Ό λͺ κ°μ **division λ¨μ**λ‘ μͺΌκ°€μ§. μλ΅ `timeline.divisions` μ λμΌν΄μΌ νλ€. | | |
| **μ μ© λ²μ:** | |
| - v1μμλ `score_context` λ₯Ό **ν΄λΉ μμ²μ λͺ¨λ μ΄λ―Έμ§μ λμΌ μ μ©**νλ€. | |
| - μ₯μ°¨ βμ΄λ―Έμ§λ§λ€ λ€λ₯Έ clef/keyβκ° νμνλ©΄ `score_context` λ°°μ΄(νμΌ μμμ λμΌ κΈΈμ΄) λ±μΌλ‘ **λ²μ μ **νλ€. | |
| ### 2.4 `options` (JSON κ°μ²΄) | |
| κΈ°λ³Έκ° μμ: | |
| ```json | |
| { | |
| "return_debug": false, | |
| "quantization": "1/8" | |
| } | |
| ``` | |
| | ν€ | νμ | κΈ°λ³Έ | μ€λͺ | | |
| |----|------|------|------| | |
| | `return_debug` | `boolean` | `false` | `true` μ΄λ©΄ `meta.debug` λ± λλ²κ·Έ μ μ© νλ ν¬ν¨ κ°λ₯. | | |
| | `quantization` | `string` | `"1/8"` | λ¦¬λ¬ μμν 그리λ. νμ©: `"1/4"`, `"1/8"`, `"1/16"`. | | |
| ### 2.5 μ©λΒ·ν¬λ§· μ ν (λΉκΈ°λ₯) | |
| - μ΄λ―Έμ§ 1μ₯ μ΅λ **10MB**, μμ² ν©κ³ μ΅λ **30MB** (ꡬνμ κΈ°μ‘΄ μμμ λ§μΆ μ μμ). | |
| - μ§μ νμ₯μ: `jpg`, `jpeg`, `png`, `webp`. | |
| - κΆμ₯: κΈ΄ λ³ **4096px** μ΄ν λ±(ꡬν체 κΆκ³ ). | |
| --- | |
| ## 3) μλ΅ (Response) β HTTP 200 | |
| **Content-Type:** `application/json` | |
| ### 3.1 μ΅μμ ꡬ쑰 | |
| ```json | |
| { | |
| "request_id": "uuid", | |
| "source": { "total_images": 2, "filenames": ["oneline_0.png", "oneline_1.png"] }, | |
| "score_context": { "clef": "treble", "key_signature": { "fifths": -1 }, "time_signature": "4/4", "tempo_bpm_reference": null, "divisions": 4, "source": "client" }, | |
| "timeline": { "divisions": 4, "time_signature": "4/4", "tempo_bpm_reference": null }, | |
| "melody": { | |
| "voice_id": "melody1", | |
| "reduction_rule": "top_note_max_two_parts", | |
| "events": [] | |
| }, | |
| "segment_map": [], | |
| "warnings": [], | |
| "meta": {} | |
| } | |
| ``` | |
| ### 3.2 νλ μ€λͺ | |
| - **`request_id`:** μΆμ Β·λ‘κ·Έμ© UUID. | |
| - **`source`:** μ λ ₯ μμ½. `filenames` λ μ λ‘λ μμμ λμΌν μλ³Έ νμΌλͺ (μμΌλ©΄ μλ²κ° λΆμ¬ν placeholder κ°λ₯). | |
| - **`score_context`:** μμ²μ μ κ·νΒ·μμ½ν κ°. `source: "client"` κ³ μ (μλ²κ° μ‘°μ±μ μ΄λ―Έμ§μμ μ½μ§ μμμ λͺ μ). | |
| - **`timeline`:** μ μ λ¦¬λ¬ ν΄μ κΈ°μ€. `divisions` λ **ν 4λΆμν = divisions λλΉμ ** (MusicXML κ΄λ‘μ λμΌ). | |
| - **`melody`:** λ³ν©λ **λ¨μΌ μ±λΆ** κ²°κ³Ό. | |
| - **`reduction_rule`:** νμ μ²λ¦¬ κ·μΉ. v1 κ³ μ λ¬Έμμ΄ `top_note_max_two_parts` (μ΅λ 2μ±λΆμΌ λ **μ΅κ³ μ**λ§ μ±ν). | |
| - **`segment_map`:** κ° μ λ ₯ μ΄λ―Έμ§κ° λ³ν© `events` μ μ΄λ ꡬκ°μ λμνλμ§(νΈμ§Β·νμ΄λΌμ΄νΈμ©). Β§3.4. | |
| - **`warnings`:** μΈμΒ·νμ§ κ²½κ³ μ½λ λ¬Έμμ΄ λ°°μ΄. | |
| - **`meta`:** νμ΄νλΌμΈ λͺ¨λ, μ΄λ―Έμ§λ³ μ μ²λ¦¬ μμ½, `return_debug` μ λλ²κ·Έ λ±. | |
| ### 3.3 `melody.events[]` (Event) | |
| μκ° μμλ‘ μ λ ¬. `onset_div` λ **λ³ν© ν μ μ** νμλΌμΈ κΈ°μ€. | |
| 곡ν΅: | |
| | νλ | νμ | νμ | μ€λͺ | | |
| |------|------|------|------| | |
| | `event_id` | `string` | μ | ν΄λΌμ΄μΈνΈ νΈμ§Β·ν€ μμ μ© κ³ μ ID. | | |
| | `type` | `string` | μ | `"note"` \| `"rest"` | | |
| | `duration_div` | `integer` | μ | κΈΈμ΄(division). μμ μ μ. | | |
| | `onset_div` | `integer` | μ | μμ μκ°(division). **0 μ΄μ μ μ.** | | |
| `type === "note"` μΌ λ μΆκ°: | |
| | νλ | νμ | νμ | μ€λͺ | | |
| |------|------|------|------| | |
| | `step` | `string` | μ | `C`, `D`, β¦, `B` | | |
| | `octave` | `integer` | μ | Scientific pitch octave. | | |
| | `alter` | `integer` | μ | μμν: `-1` νλ«, `0` μμ, `1` μ΅, `2` λλΈμ΅ λ±. | | |
| | `pitch_midi` | `integer` | μλμ€ | 0β127. μμΌλ©΄ μ¬μκΈ°κ° μ°μ μ¬μ© κ°λ₯. | | |
| | `confidence` | `number` | μλμ€ | 0β1 κ·Όμ¬. | | |
| | `segment_order` | `integer` | μλμ€ | μ΄ μ΄λ²€νΈκ° κΈ°μν **μ λ ₯ μ΄λ―Έμ§ μλ²**(1-based). | | |
| | `bbox` | `array` | μλμ€ | ν΄λΉ μ΄λ―Έμ§ **μλ³Έ ν½μ μ’νκ³** `[x, y, width, height]` . | | |
| `type === "rest"` μΌ λ: | |
| - `step` / `octave` / `alter` λ **ν¬ν¨νμ§ μλλ€**(λλ λͺ¨λ μλ΅). | |
| **λ³ν© κ·μΉ (λ Όλ¦¬):** | |
| - μ΄λ―Έμ§ `k` μ 첫 μ΄λ²€νΈμ `onset_div` λ, μ§μ μ΄λ―Έμ§λ€μμ κ³μ°λ **λμ κΈΈμ΄(λλ μΈμλ λ§λ κ²½κ³)** μ§νλΆν° μ΄μ΄μ§λ€. | |
| - ꡬν체λ **ν½μ Β·OMR κΈ°λ°**μΌλ‘ κ°κ²©μ μΆμ ν μ μμΌλ―λ‘, λ§μ§λ§ μκ³Ό λ€μ μ΄λ―Έμ§ 첫 μ μ¬μ΄μ **μμ gap** μ΄ μκΈ°λ©΄ `warnings` μ μ½λλ‘ λ¨κΈΈ μ μλ€. | |
| ### 3.4 `segment_map[]` | |
| κ° μ λ ₯ μΈκ·Έλ¨ΌνΈλ³ λ©ν: | |
| | νλ | νμ | μ€λͺ | | |
| |------|------|------| | |
| | `segment_id` | `string` | μ: `seg1` | | |
| | `order` | `integer` | 1-based, μ λ‘λ μμ. | | |
| | `filename` | `string` | μλ³Έ νμΌλͺ . | | |
| | `width` | `integer` | μλ³Έ μ΄λ―Έμ§ λλΉ. | | |
| | `height` | `integer` | μλ³Έ μ΄λ―Έμ§ λμ΄. | | |
| | `event_index_range` | `object` | `start` / `end` (inclusive) β `melody.events` λ°°μ΄ μΈλ±μ€. λ³ν© κ²°κ³Όκ° λΉμ΄ μμΌλ©΄ λμΌ μΈλ±μ€λ‘ νν κ°λ₯. | | |
| ### 3.5 `warnings` (μμ μ½λ) | |
| μλ²λ νμν λ§νΌ μΆκ°ν μ μλ€. μ: | |
| - `staff_count_mismatch_expected_one`: ν μ΄λ―Έμ§μμ κ²μΆλ staff νλ³΄κ° 1μ΄ μλ. | |
| - `line_break_between_images`: λ€μ€ μ΄λ―Έμ§ λ³ν© μ κ²½κ³ ν΄λ¦¬μ€ν± μ¬μ©. | |
| - `timing_from_pixel_gaps_heuristic`: 리λ¬μ΄ ν½μ κ°κ²© μΆμ μ μμ‘΄. | |
| - `annotation_overlap_detected`: λμΒ·μ λ±μ΄ μνμ κ²ΉμΉ κ°λ₯. | |
| - `neural_omr_model_unavailable`: μ κ²½λ§ λΉνμ±/μ€ν¨ ν ν΄λ°±. | |
| - `large_deskew_correction_applied`: ν° κΈ°μΈκΈ° 보μ . | |
| ### 3.6 `meta` (κΆμ₯ νμ νλ) | |
| - `pipeline_mode`: `string` β μ: `single_staff_v1`. | |
| - `preprocess.segments[]`: μ΄λ―Έμ§λ³ μ μ²λ¦¬ μμ½(μλ³Έ/μμ ν΄μλ, deskew κ°λ, μ‘°λͺ μ κ·ν μ¬λΆ λ±). κΈ°μ‘΄ ꡬνκ³Ό λμΌν **μ 보 μ±κ²©**μ μ μ§νλ, νλλ ꡬνμ λ§κ² μ±μ΄λ€. | |
| - `debug`: `options.return_debug === true` μΌ λλ§. | |
| --- | |
| ## 4) μ€λ₯ μλ΅ | |
| | HTTP | 쑰건 | | |
| |------|------| | |
| | `400` | νμΌ λλ½, `score_context` λλ½/νμ± μ€ν¨, μλͺ»λ `options` | | |
| | `413` | μ©λ μ΄κ³Ό | | |
| | `415` | λ―Έμ§μ νμ₯μ | | |
| | `422` | μ΄λ―Έμ§λ μμΌλ **μ λ’°ν λ§ν λ¨μΌ μ€νν**λ₯Ό λ§λ€ μ μμ, λλ μ ν¨ν μ΄λ²€νΈκ° μμ | | |
| | `500` | μλ² λ΄λΆ μ€λ₯ | | |
| μ€λ₯ λ°λλ FastAPI κΈ°λ³Έ `detail` λλ μλ ννμ JSON μ€ νλλ‘ ν΅μΌν μ μλ€(ꡬν μ ν κ°μ§λ‘ κ³ μ κΆμ₯): | |
| ```json | |
| { | |
| "error": { | |
| "code": "UNPROCESSABLE_STAFF_LAYOUT", | |
| "message": "Expected exactly one staff per image.", | |
| "details": { "segment_order": 2, "detected_staff_candidates": 2 } | |
| } | |
| } | |
| ``` | |
| --- | |
| ## 5) ꡬνΒ·νμ§ λ‘λλ§΅ (μμ½) | |
| 1. λ¨μΌ μ€νν ROI μ κ·ν(곑μ μ€μ 보μ μ λ¨κ³μ λμ κ°λ₯). | |
| 2. λ ΈνΈΒ·μΌνΒ·beam λ± μΈμ(λ₯λ¬λ + κ·μΉ ν΄λ°±). | |
| 3. νμ β μ΅κ³ μ 리λμ . | |
| 4. λ€μ€ μ΄λ―Έμ§ μμ λ³ν© λ° `segment_map` / `segment_order` μ±μ°κΈ°. | |
| 5. νκ· ν μ€νΈ: `sample_imgs/sample_oneline_0.png`, `sample_imgs/sample_oneline_chord.png` λ° μ°μ λ μ₯ μλ리μ€. | |
| --- | |
| ## 6) λͺ μμ μΌλ‘ νμ§ μλ κ² (v1) | |
| - λ€μ€ μ€νν μ 보 νμ μΆμ (`a`/`b`/`c` λ±) λ° λ°μ£Ό λΆλ¦¬. | |
| - μ±λΆ λΌλ²¨ `soprano`/`alto` λ± μλ μΆμ . | |
| - MusicXMLμ HTTP μλ΅ λ³Έλ¬ΈμΌλ‘ λ°ν. | |
| - μ΄λ―Έμ§μμ clef/key μλ νλ (ν΄λΌμ΄μΈνΈ μ κ³΅μ΄ SSOT). | |
| --- | |
| ## 7) SSOT λ³κ²½ μ μ°¨ | |
| μ€ν€λ§Β·μλ―Έ λ³κ²½ μ **λ°λμ** λ€μμ ν¨κ» κ°±μ νλ€. | |
| 1. μ΄ λ¬Έμ (`docs/analyze-api-spec.md`) | |
| 2. `docs/frontend-integration-guide.md` | |
| 3. `docs/analyze-response-format-examples.json` | |