vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
One brightfield image in. Four bound outputs, and a record you can verify.
cultureQC reads a phase-contrast image of a cell culture and returns a confluency
estimate, a QC flag with visual evidence of where it is looking, a recommended
action, and a hash-chained audit record — from a single call.
It is built for automation, not the bench: something calls analyze() per
captured image, and a human only steps in when it flags an exception or audits
the trail afterwards.README 전문 보기 ↗One brightfield image in. Four bound outputs, and a record you can verify.
cultureQC reads a phase-contrast image of a cell culture and returns a confluency estimate, a QC flag with visual evidence of where it is looking, a recommended action, and a hash-chained audit record — from a single call.
from culture.pipeline import analyze
record = analyze("field.tif", flask_id="F-01", cell_line="Huh7")
record["confluency_pct"] # 13.45
record["qc_flag"] # "contamination_suspected"
record["recommended_action"] # "human_review"
record["record_hash"] # sha256 over the canonical JSON, linked to the previous record
It is built for automation, not the bench: something calls analyze() per
captured image, and a human only steps in when it flags an exception or audits
the trail afterwards.
Classical thresholding measures brightness, and confluency is not brightness. On the same benchmark, thresholding is off by ~31 percentage points where this is off by 2.3 — the difference between passaging a flask and doing nothing.
The differentiator is not raw accuracy though; it is explainability and auditability. Comparable tools show a mask and a number. This draws the Grad-CAM regions that caused the flag, and writes every decision to a tamper-evident log you can re-verify later.
analyze() returns all four in one record, so a number, its evidence, the
decision it drove, and the proof cannot drift apart.
| Output | Detail |
|---|---|
| Confluency | Cellpose-SAM (cpsam_v2) probability map. 2.3 pp mean absolute error, zero-shot across morphologies. A threshold baseline is computed alongside for comparison. |
| QC flag | EfficientNet-B0 over a centred 256×256 tile: normal, contamination_suspected, detachment, image_quality. 98% test accuracy, 100% contamination recall at every severity. |
| Evidence | Up to 8 Grad-CAM bounding boxes showing the regions behind the flag, mapped back to full-image coordinates. |
| Action | Deterministic rules over confluency + flag + timing: passage, feed, hold, human_review. No model decides this. |
Each analysis is appended to a hash-chained JSONL log (culture/records.py,
28-field schema in culture/schema.json). Every record carries the SHA-256 of
the one before it, so altering any record breaks every link after it.
python -m culture.records events.jsonl
culture/ the library — this is the product
pipeline.py analyze(): image -> record. The integration surface.
seg.py confluency (Cellpose-SAM, plus a threshold baseline)
qc.py the classifier and its Grad-CAM evidence
rules.py deterministic action decisions
records.py hash-chained JSONL writer + verifier
rationale.py plain-language explanation of a decision
schema.json the output contract
deploy/ how it ships
hf-space/ FastAPI service; the HuggingFace Space, pushable as-is
hf-space-demo/ Gradio + ZeroGPU demo Space, raw model output, no product UI
sync_space.py mirrors culture/ + config/ into both Spaces
publish_space.py, publish_demo_space.py push each Space via the Hub API
README.md deployment runbook
site/ the landing page — React + Vite
src/ components, hooks, styles.css + refinements.css + instrument.css
src/data.json generated: real pipeline output the page renders
assets/ source images, fonts and the scripts that build them
public/ what ships as-is: og.png, favicon.png, img/
demo/app.py Gradio console, the human review surface
scripts/ dataset prep and contamination synthesis
config/lines/ per-cell-line thresholds
test-data/ real phase-contrast tiles + synthetic challenge cases
docs/
DATA.md training data: what it is, how to regenerate it
audit_mapping.md record fields mapped to audit requirements
PRODUCT.md, DESIGN.md, UI_PLAN.md working documents for the demo surfaces
Training data is not tracked — it is downloaded and derived, and regenerating it
is two commands. See docs/DATA.md. Nothing in the library, the
API or the site build depends on it.
python -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python -c "from culture.pipeline import analyze; print(analyze('test-data/contam_00015.png'))"
Model weights download on first use — Cellpose-SAM from its own package, the QC
classifier from LongGrainRice/cultureqc-qc-effnetb0-v1.
Expect a slow first call.
Run the page and the API together. Two processes; the dev server proxies the API calls, so the browser sees one origin:
python deploy/hf-space/api.py # http://127.0.0.1:7860
cd site && npm install && npm run dev # http://localhost:5173
Run the console:
python demo/app.py
python -m pytest deploy/hf-space/test_api.py -q # 23 API contract tests
These cover readiness semantics, input validation, the response contract, the
evidence-box arithmetic, the CORS allowlist, and that concurrent uploads cannot
break the hash chain. Models are stubbed — the concurrency test is the exception
and drives the real RecordWriter, because the bug it guards is in the write
path rather than in inference.
Because the models are stubbed, the suite needs no torch and no cellpose and
runs in seconds. .github/workflows/tests.yml lists the light dependency set,
and adds two drift checks on every push: that the Space mirror still matches
culture/, and that site/src/data.json still matches the pipeline output it
was generated from.
Stated here rather than discovered later.
The QC classifier is trained entirely on synthetic contamination, and the 100% recall above is measured at the sprites' native scale. Bacterial sprites are composited onto real microscopy at their source optical scale, which is larger relative to the cells than a real objective would show, so that figure is bounded to that distribution rather than to real contaminated cultures. Rescaling the sprite library and re-running the shipped classifier: at 0.45× only 6 of 12 contaminated tiles in the severity ladder were flagged, and at 0.2× the model reads contamination as an image-quality artifact instead.
Analysis is slow on CPU. About a minute per image on 2 vCPU; roughly 20 s on
a fast laptop. A GPU changes this by about an order of magnitude, and
culture/seg.py selects the device automatically.
demo/app.py::_scale_bboxes over-scales evidence boxes on images larger than
256px. The Grad-CAM box is measured on a centred crop, so mapping it back should
offset the origin only — the box's own width and height stay in tile pixels. The
console still multiplies them by the full-image factor, drawing a box far wider
than the region actually read. deploy/hf-space/api.py has the correct mapping.
The hosted audit chain is per-boot. HuggingFace Space storage is wiped on
restart, so prev_record_hash links within one boot only. Records stay
individually verifiable, and the browser builds its own chain over an upload
batch. A durable chain needs persistent storage and CULTUREQC_LOG=/data/....
The page is static on Vercel; the pipeline runs as a Docker Space on HuggingFace. They are independent deployments joined by one URL, so neither can break the other's build, and the page still hashes files and builds a verifiable manifest when the API is asleep.
See deploy/README.md.
React 18 on Vite, in site/. Nine routes behind a hash router (see
site/README.md for the table), and the
pieces worth naming: Plate (the specimen viewer with its mask, evidence boxes
and scan sweep), Upload (drop, unzip, hash, analyse, verify — all in the
browser), and Chain (recomputes nine SHA-256 digests on demand, and breaks
them on purpose so you can watch it fail).
cd site
npm install
npm run dev # http://localhost:5173, proxying /health and /analyze to :7860
npm run build # -> site/dist, which is what Vercel serves
npm run data # regenerate src/data.json + public/img from pipeline output
Two rules the code depends on. site/src/styles.css drives state through
data-* attributes and CSS custom properties ([data-mask], [data-flagged],
--nw), so renaming a class or an attribute in a component breaks the design
silently. And src/data.json is generated by
site/assets/build_data.py from committed pipeline output — editing it by hand
would make the page show records that never existed.
MIT — see LICENSE.