swaraj commited on
Commit
def74fd
Β·
1 Parent(s): ac71316
Files changed (1) hide show
  1. model_interface/a_17_grapes_count.py +100 -92
model_interface/a_17_grapes_count.py CHANGED
@@ -425,103 +425,111 @@ def grapes_count():
425
  "b_ratio": b_ratio,
426
  })
427
 
428
- # ── STABLE two-column layout (always rendered, prevents shaking) ───
429
- # Read the input image dimensions so the placeholder matches exactly.
430
  _probe = cv2.imread(image_path)
431
- if _probe is not None:
432
- _ih, _iw = _probe.shape[:2]
433
- # Columns are ~50% of page width. Use aspect-ratio CSS so the
434
- # placeholder box is the same proportional height as the real image.
435
- _aspect_pct = round((_ih / _iw) * 100, 2)
436
- else:
437
- _aspect_pct = 75.0 # safe fallback (4:3)
438
-
439
- col_input, col_output = st.columns(2)
440
-
441
- with col_input:
442
- st.subheader(f"πŸ“· Input β€” {selected_image}")
443
- st.image(image_path, use_container_width=True)
444
-
445
- with col_output:
446
- st.subheader("πŸ” Predicted Output")
447
- # Use a single st.empty() slot β€” Streamlit replaces its content
448
- # in-place on rerun, so the surrounding layout never shifts.
449
- out_slot = st.empty()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  if st.session_state["result"] is not None:
451
- out_img = st.session_state["result"]["image"]
452
- if os.path.exists(out_img):
453
- out_slot.image(out_img, use_container_width=True)
454
  else:
455
- # Aspect-ratio box matches the input image height exactly.
456
- out_slot.markdown(
457
- f"""
458
- <div style="position:relative;width:100%;
459
- padding-top:{_aspect_pct}%;
460
- background:#f0f2f6;border-radius:8px;">
461
- <div style="position:absolute;inset:0;display:flex;
462
- align-items:center;justify-content:center;
463
- color:#888;font-size:15px;">
464
- Click <strong>Run</strong> to see prediction here
465
- </div>
466
- </div>
467
- """,
468
- unsafe_allow_html=True,
469
- )
470
 
471
- # ── results section ────────────────────────────────────────────────
472
- if st.session_state["result"] is not None:
473
- res = st.session_state["result"]
474
- score = st.session_state["score"]
475
- grade_label = st.session_state["grade_label"]
476
- breakdown = st.session_state["breakdown"]
477
-
478
- st.success("βœ… Prediction complete!")
479
- st.markdown("---")
480
- st.header("πŸ“Š Evaluation Results")
481
-
482
- m1, m2, m3, m4 = st.columns(4)
483
- m1.metric("Total Grapes", res["summary"]["total"])
484
- m2.metric("Green / Black",
485
- f'{res["summary"]["green"]} / {res["summary"]["black"]}')
486
- m3.metric("QC Score", f"{score} / 100")
487
- m4.metric("Grade", grade_label)
488
-
489
- t1, t2 = st.columns(2)
490
- with t1:
491
- st.subheader("QC Penalty Breakdown")
492
- st.dataframe(pd.DataFrame(breakdown), use_container_width=True)
493
- with t2:
494
- st.subheader("Detection Summary")
495
- st.json(res["summary"])
496
-
497
- st.subheader("πŸ“„ Raw Detection Data")
498
- st.dataframe(res["df"], use_container_width=True)
499
-
500
- st.markdown("---")
501
- st.header("πŸ“₯ Export Reports")
502
- pdf_path = generate_pdf_report(
503
- image_path, res, score, grade_label, breakdown, selected_template
504
- )
 
505
 
506
- dl1, dl2 = st.columns(2)
507
- with dl1:
508
- with open(res["excel"], "rb") as f:
509
- st.download_button(
510
- label="⬇ Download Excel Data",
511
- data=f,
512
- file_name="grape_analysis.xlsx",
513
- mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
514
- use_container_width=True,
515
- )
516
- with dl2:
517
- with open(pdf_path, "rb") as f:
518
- st.download_button(
519
- label="πŸ“„ Download PDF Report",
520
- data=f,
521
- file_name=f"QC_Report_{selected_template}.pdf",
522
- mime="application/pdf",
523
- use_container_width=True,
524
- )
525
 
526
 
527
  if __name__ == "__main__":
 
425
  "b_ratio": b_ratio,
426
  })
427
 
428
+ # ── Read input image aspect ratio once ────────────────────────────
 
429
  _probe = cv2.imread(image_path)
430
+ _aspect_pct = round((_probe.shape[0] / _probe.shape[1]) * 100, 2) \
431
+ if _probe is not None else 75.0
432
+
433
+ # ── Tabs: Detection | Results β€” both always exist in the DOM ──────
434
+ # Using tabs means the results panel is NEVER below the images, so
435
+ # it can never push the image columns up and cause shaking.
436
+ tab_detect, tab_results = st.tabs(["πŸ” Detection", "πŸ“Š Results & Export"])
437
+
438
+ # ── TAB 1: Detection ──────────────────────────────────────────────
439
+ with tab_detect:
440
+ col_input, col_output = st.columns(2)
441
+
442
+ with col_input:
443
+ st.subheader(f"πŸ“· Input β€” {selected_image}")
444
+ st.image(image_path, use_container_width=True)
445
+
446
+ with col_output:
447
+ st.subheader("πŸ” Predicted Output")
448
+ out_slot = st.empty()
449
+ if st.session_state["result"] is not None:
450
+ out_img = st.session_state["result"]["image"]
451
+ if os.path.exists(out_img):
452
+ out_slot.image(out_img, use_container_width=True)
453
+ else:
454
+ out_slot.markdown(
455
+ f"""
456
+ <div style="position:relative;width:100%;
457
+ padding-top:{_aspect_pct}%;
458
+ background:#1e1e1e;border-radius:8px;
459
+ border:1px dashed #444;">
460
+ <div style="position:absolute;inset:0;display:flex;
461
+ align-items:center;justify-content:center;
462
+ color:#888;font-size:15px;">
463
+ Click <strong>β–Ά Run</strong> in the sidebar
464
+ </div>
465
+ </div>
466
+ """,
467
+ unsafe_allow_html=True,
468
+ )
469
+
470
+ # Status bar at bottom of detection tab β€” never causes layout shift
471
  if st.session_state["result"] is not None:
472
+ st.success(f"βœ… Detection complete β€” "
473
+ f"{st.session_state['result']['summary']['total']} grapes found. "
474
+ f"Switch to the **Results & Export** tab for full analysis.")
475
  else:
476
+ st.info("Press **β–Ά Run Prediction & Grade** in the sidebar to start.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
 
478
+ # ── TAB 2: Results & Export ───────────────────────────────────────
479
+ with tab_results:
480
+ if st.session_state["result"] is None:
481
+ st.info("Run a prediction first to see results here.")
482
+ else:
483
+ res = st.session_state["result"]
484
+ score = st.session_state["score"]
485
+ grade_label = st.session_state["grade_label"]
486
+ breakdown = st.session_state["breakdown"]
487
+
488
+ st.header("πŸ“Š Evaluation Results")
489
+ m1, m2, m3, m4 = st.columns(4)
490
+ m1.metric("Total Grapes", res["summary"]["total"])
491
+ m2.metric("Green / Black",
492
+ f'{res["summary"]["green"]} / {res["summary"]["black"]}')
493
+ m3.metric("QC Score", f"{score} / 100")
494
+ m4.metric("Grade", grade_label)
495
+
496
+ st.markdown("---")
497
+ t1, t2 = st.columns(2)
498
+ with t1:
499
+ st.subheader("QC Penalty Breakdown")
500
+ st.dataframe(pd.DataFrame(breakdown), use_container_width=True)
501
+ with t2:
502
+ st.subheader("Detection Summary")
503
+ st.json(res["summary"])
504
+
505
+ st.subheader("πŸ“„ Raw Detection Data")
506
+ st.dataframe(res["df"], use_container_width=True)
507
+
508
+ st.markdown("---")
509
+ st.header("πŸ“₯ Export Reports")
510
+ pdf_path = generate_pdf_report(
511
+ image_path, res, score, grade_label, breakdown, selected_template
512
+ )
513
 
514
+ dl1, dl2 = st.columns(2)
515
+ with dl1:
516
+ with open(res["excel"], "rb") as f:
517
+ st.download_button(
518
+ label="⬇ Download Excel Data",
519
+ data=f,
520
+ file_name="grape_analysis.xlsx",
521
+ mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
522
+ use_container_width=True,
523
+ )
524
+ with dl2:
525
+ with open(pdf_path, "rb") as f:
526
+ st.download_button(
527
+ label="πŸ“„ Download PDF Report",
528
+ data=f,
529
+ file_name=f"QC_Report_{selected_template}.pdf",
530
+ mime="application/pdf",
531
+ use_container_width=True,
532
+ )
533
 
534
 
535
  if __name__ == "__main__":