Skip to content

feat(dashboard): add Grad-CAM heatmap overlay visualizer#106

Merged
jpdevhub merged 4 commits into
jpdevhub:mainfrom
Deepak06-v:feat/gradcam-overlay
Jun 18, 2026
Merged

feat(dashboard): add Grad-CAM heatmap overlay visualizer#106
jpdevhub merged 4 commits into
jpdevhub:mainfrom
Deepak06-v:feat/gradcam-overlay

Conversation

@Deepak06-v

@Deepak06-v Deepak06-v commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Description

Closes #46
This PR resolves the issue by integrating the Grad-CAM activation map overlay visualization on the scan analysis page (AnalysisDashboard.tsx).

Key changes:

  • Retrieves photo_url from the scan result and fetches the image in the frontend, sending it to the backend /api/v1/gradcam endpoint to generate the Grad-CAM base64 JPEG heatmap.
  • Dynamically splits the desktop layout when a photo is available to display a Scan Visualization card on the left and the Score Card / Species Panel stacked on the right. If no photo is available, falls back to the original side-by-side layout.
  • Renders the scanned fish with viewfinder corners and stacks the Grad-CAM heatmap overlay on top of it.
  • Added interactive controls to the visualization card:
    • Toggles: Quick-buttons to switch between ORIGINAL (0% opacity), BLENDED (50% opacity), and HEATMAP (100% opacity).
    • Blend Slider: A range slider to adjust opacity smoothly from 0% to 100%.
    • Color Legend: Shows a LOW → HIGH color gradient bar when the heatmap is visible.
    • State Overlays: Styled overlays for loading (spinner) and error (retry button) states.

Checklist

  • npm run lint passes with no errors
  • npm run build compiles without TypeScript errors
  • python -m pytest passes (including new tests I added)
  • No .env files, API keys, secrets, model weights, or __pycache__ in this diff
  • Branch is rebased on main, not merged

Summary by CodeRabbit

  • New Features
    • Analysis Dashboard now displays Grad-CAM heatmap overlays when a photo is available, visualizing model attention areas.
    • Added blend opacity controls (toggle and slider) plus a legend for easier interpretation.
    • Improved analysis experience with loading states, error handling, and a retry action.
  • UI Updates
    • Reworked the score/species area into a visualization-first layout when photos are present.

@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

@Deepak06-v is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jpdevhub, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 39 minutes and 15 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a701fe0-2c48-453b-a6dc-bd0abf14a180

📥 Commits

Reviewing files that changed from the base of the PR and between 8b19851 and fbf912d.

📒 Files selected for processing (1)
  • src/pages/AnalysisDashboard.tsx

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

AnalysisDashboard gains a Grad-CAM heatmap overlay feature: new state tracks the heatmap image, loading/error status, blend opacity, and a retry counter. A useEffect fetches the scan image, calls api.getGradcam, and stores the result. The UI is restructured to render an image visualization card with toggle, blend slider, legend, and retry flow when a photo_url exists, with a fallback to the previous stats-only layout.

Changes

Grad-CAM Heatmap Visualization

Layer / File(s) Summary
State declarations and icon imports
src/pages/AnalysisDashboard.tsx
Adds lucide-react icons for the overlay UI and declares component state for Grad-CAM image, loading/error status, blend opacity, retry trigger, and derived photo_url.
Grad-CAM fetch effect and retry handler
src/pages/AnalysisDashboard.tsx
Introduces a useEffect that downloads the scan image as a blob, calls api.getGradcam, stores gradcam_image, and manages loading/error state with an isMounted cleanup guard; adds handleRetry to re-trigger the effect.
Grad-CAM overlay layout and controls
src/pages/AnalysisDashboard.tsx
Reworks the main render path to display a stacked visualization card (original image + conditional heatmap overlay) with toggle buttons, blend opacity slider and range input, a legend overlay, and dedicated loading/error/retry UI when photo_url is present; retains a fallback to the prior stats-only card layout when no photo_url is available.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 Hippity-hop, what do I see?
A heatmap glowing, warm as tea!
The fish's freshness, pixel-bright,
Blended layers, just right.
Retry with a click, the colors flow—
GradCAM's magic puts on a show! 🐠

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main feature addition: a Grad-CAM heatmap overlay visualizer for the dashboard.
Linked Issues check ✅ Passed The implementation fully addresses issue #46 requirements: retrieves base64 heatmap from /api/v1/gradcam endpoint, builds an overlay component, and provides toggle controls between original and heatmap views.
Out of Scope Changes check ✅ Passed The PR focuses exclusively on Grad-CAM visualization in AnalysisDashboard with necessary icon imports; no unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jpdevhub

Copy link
Copy Markdown
Owner

Give the issue no.

@Deepak06-v

Copy link
Copy Markdown
Contributor Author

Give the issue no.

46

@jpdevhub

Copy link
Copy Markdown
Owner

This is an incredible implementation! The slider and the mix-blend-multiply CSS overlay are exactly what we wanted. However, because main moved fast this week, your branch is out of date and currently reverts our recent bug fixes for the Offline Mode in AnalysisDashboard.tsx. Please git pull origin main to sync your branch and restore the offlineScanResult fallback block in the load() function. Once that's synced, we will merge this immediately

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/pages/AnalysisDashboard.tsx (2)

80-125: ⚡ Quick win

Consider adding AbortController for request cancellation.

The effect uses isMounted to prevent state updates after unmount, but in-flight requests are not cancelled. If the user navigates away or clicks retry rapidly, previous requests continue unnecessarily. Adding an AbortController improves resource efficiency and prevents potential race conditions with rapid retries.

♻️ Proposed refactor with AbortController
   useEffect(() => {
     if (!photo_url) return;
     const url = photo_url;

     let isMounted = true;
+    const controller = new AbortController();

     async function loadGradcam() {
       setGradcamLoading(true);
       setGradcamError(null);

       try {
-        const res = await fetch(url);
+        const res = await fetch(url, { signal: controller.signal });

         if (!res.ok) {
           throw new Error(`Failed to download scan image (${res.status})`);
         }

         const blob = await res.blob();
         const gradcamRes = await api.getGradcam(blob);

         if (isMounted) {
           setGradcamImage(gradcamRes.gradcam_image);
         }
       } catch (err) {
+        if (err instanceof DOMException && err.name === 'AbortError') {
+          return; // Request was cancelled, no error handling needed
+        }
         console.error("Grad-CAM generation error:", err);

         if (isMounted) {
           setGradcamError(
             err instanceof Error
               ? err.message
               : "Heatmap generation failed."
           );
         }
       } finally {
         if (isMounted) {
           setGradcamLoading(false);
         }
       }
     }

     loadGradcam();

     return () => {
       isMounted = false;
+      controller.abort();
     };
   }, [photo_url, retryTrigger]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/AnalysisDashboard.tsx` around lines 80 - 125, The useEffect hook in
the loadGradcam function uses isMounted flag to prevent state updates after
unmount, but in-flight network requests (the fetch call and the api.getGradcam
call) are not being cancelled, wasting resources on unnecessary requests if the
user navigates away or retries rapidly. Add an AbortController instance at the
beginning of the loadGradcam function, pass its signal to both the fetch call
and the api.getGradcam call, and then call the abort method on the controller in
the cleanup function (where isMounted is currently set to false) to ensure all
pending requests are properly cancelled when the effect is cleaned up.

319-364: ⚖️ Poor tradeoff

Consider extracting duplicated score card and species panel into reusable components.

The score card (lines 319-364 vs 411-456) and species panel (lines 367-405 vs 459-497) are nearly identical across both layout branches. Extracting these into <ScoreCard> and <SpeciesPanel> components would reduce duplication and simplify future maintenance.

Also applies to: 411-456

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/AnalysisDashboard.tsx` around lines 319 - 364, The GlassCard
component displaying the score information with grade, freshness_index,
classification, and confidence fields is duplicated across multiple layout
branches in the file. Create two new reusable components: extract the GlassCard
structure (starting with className "p-8 relative overflow-hidden") into a
ScoreCard component that accepts props for grade, freshness_index,
classification, and confidence, and similarly extract the species panel
structure into a SpeciesPanel component. Then replace all duplicated instances
of these card layouts with calls to the new components, passing the appropriate
data as props to eliminate the duplication across the different layout branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/AnalysisDashboard.tsx`:
- Around line 221-228: The className on the div element wrapping the color
legend map contains an invalid Tailwind z-index utility class `z-25`, which is
not part of the standard Tailwind scale (z-0, z-10, z-20, z-30, z-40, z-50).
Replace `z-25` with `z-30` to ensure the legend layers correctly above overlays
and properly apply z-index styling.

---

Nitpick comments:
In `@src/pages/AnalysisDashboard.tsx`:
- Around line 80-125: The useEffect hook in the loadGradcam function uses
isMounted flag to prevent state updates after unmount, but in-flight network
requests (the fetch call and the api.getGradcam call) are not being cancelled,
wasting resources on unnecessary requests if the user navigates away or retries
rapidly. Add an AbortController instance at the beginning of the loadGradcam
function, pass its signal to both the fetch call and the api.getGradcam call,
and then call the abort method on the controller in the cleanup function (where
isMounted is currently set to false) to ensure all pending requests are properly
cancelled when the effect is cleaned up.
- Around line 319-364: The GlassCard component displaying the score information
with grade, freshness_index, classification, and confidence fields is duplicated
across multiple layout branches in the file. Create two new reusable components:
extract the GlassCard structure (starting with className "p-8 relative
overflow-hidden") into a ScoreCard component that accepts props for grade,
freshness_index, classification, and confidence, and similarly extract the
species panel structure into a SpeciesPanel component. Then replace all
duplicated instances of these card layouts with calls to the new components,
passing the appropriate data as props to eliminate the duplication across the
different layout branches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d451b1ec-31ac-4f88-8d58-25713e4c88a6

📥 Commits

Reviewing files that changed from the base of the PR and between e84b7d3 and 53eaeb1.

📒 Files selected for processing (1)
  • src/pages/AnalysisDashboard.tsx

Comment thread src/pages/AnalysisDashboard.tsx Outdated
@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fresh-scan-ai Ready Ready Preview, Comment Jun 18, 2026 4:28pm

@jpdevhub jpdevhub left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing

@jpdevhub jpdevhub merged commit 8afaf03 into jpdevhub:main Jun 18, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M-04: GradCAM Visual Explanation UI

2 participants