Morph and Split is a user-friendly tool for computer vision and machine learning practitioners to preprocess, augment, and split image-mask datasets into training, validation, and test sets. It supports both stratified and non-stratified splitting workflows, to maintain class balance across training, validation and test sets.
- Frontend: Hosted on Vercel or runnable locally
- Backend: Deployable on Google Cloud Run or runnable locally
- Storage: Uses Google Cloud Storage (GCS) buckets.
Check out the live application here: Morph and Split
Experience the tool in action with real-time image augmentation and dataset splitting.
.
├── backend
│ ├── app
│ │ ├── config
│ │ ├── __init__.py
│ │ ├── routes
│ │ ├── services
│ │ └── utils
│ ├── cors.json
│ ├── Dockerfile
│ ├── morph-and-split-toolkit-key.json
│ ├── requirements.txt
│ └── requirements_with_gpu.txt
├── docker-compose.yml
├── frontend
│ ├── Dockerfile
│ ├── eslint.config.js
│ ├── index.html
│ ├── package.json
│ ├── package-lock.json
│ ├── public
│ │ ├── assets
│ │ └── vite.svg
│ ├── src
│ │ ├── assets
│ │ ├── components
│ │ ├── entities
│ │ ├── hooks
│ │ ├── index.css
│ │ ├── main.tsx
│ │ ├── pages
│ │ ├── routes.tsx
│ │ ├── services
│ │ ├── store
│ │ ├── theme.ts
│ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.app.tsbuildinfo
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ ├── tsconfig.node.tsbuildinfo
│ ├── vercel.json
│ └── vite.config.ts
├── gc_authentication_setup.md
├── project_structure.txt
├── README.md
└── screenshots
Morph and Split is built with a modern, cloud-friendly stack for performance, scalability, and ease of use.
- React (TypeScript) – Interactive, component-based UI
- Vite – Fast bundler and dev server
- Chakra UI – Modular and accessible UI components
- Zustand – Lightweight state management
- React Router – Client-side routing
- React Query – Server-state management and caching
- Axios – HTTP client for API requests
- CSS – Custom styling and Chakra UI overrides
- Vercel – Hosting and deployment of the frontend
- Python 3.10+
- Flask – Lightweight backend framework
- TensorFlow – Powering the image augmentation pipeline
- scikit-image – Advanced image processing tools
- Google Cloud Storage (GCS) – Storage for images, masks, and augmented data
- Google Secret Manager – Securely manages secrets (e.g., for signed URL key)
- Google Cloud Run – Serverless backend deployment
- Workload Identity Federation (WIF) – Secure local-to-cloud authentication
- Service Account Impersonation – Enables secure GCP resource access via Gmail identity
- Docker – Backend containerization for reproducibility and deployment
- Git – Version control
- Google Cloud SDK (gcloud CLI) – Infrastructure and service configuration
- Docker Hub – Hosting backend container images
Morph and Split uses Workload Identity Federation (WIF) and Service Account Impersonation to securely access Google Cloud Storage and Secret Manager from both local development environments and the deployed backend on Cloud Run. This means:
- No hardcoded credentials
- No need to download service account key files
Instead, when running locally, your Gmail identity impersonates the service account using WIF. When running on Google Cloud Run, the service account directly authenticates and accesses resources securely.
Whether locally or in production, the backend can:
- Create and delete buckets
- Upload/download/delete files in GCS
- Generate signed URLs for uploads and downloads
- Access secrets securely from Secret Manager
You can run Morph and Split with:
- Backend on your local machine or deployed to Google Cloud Run
- Frontend on Vercel or your local machine
- Data stored in your Google Cloud Storage buckets
To enable this, you will need to set up:
- A Google cloud account
- A Google cloud project
- A Service Account
- Workload Identity Federation, and
- Service Account Impersonation
We will use the GCloud CLI for most of the setup.
Here is a placeholder reference table:
# | Item | Placeholder | Example |
---|---|---|---|
1 | Gmail Address | YOUR_GMAIL_ADDRESS | aiheonye@gmail.com |
2 | Project ID | YOUR_PROJECT_NAME | morph-and-split-app |
3 | Service Account Name | YOUR_SERVICE_ACCOUNT_NAME | morph-and-split-app-sa |
4 | Workload Identity Pool Name | YOUR_WORKLOAD_IDENTITY_POOL_NAME | morph-and-split-wif |
5 | Workload Identity Provider Name | YOUR_WORKLOAD_IDENTITY_PROVIDER_NAME | morph-and-split-provider |
6 | Secret Name | YOUR_SECRET_NAME | GCS_SIGNED_URL_KEY |
- Python 3.10+
- Node.js 20.15+
- npm 10.7.0+
- Google Cloud SDK (gcloud)
- Docker
First, clone the Morph and Split repo to your local machine:
git clone https://github.com/anthony-iheonye/morph-and-split.git
cd morph-and-split
cd backend
python3 -m venv ms_venv # create a virtual environment, ms_venv.
source ms_venv/bin/activate # activate the ms_venv
pip install --upgrade pip # upgrade pip
pip install -r requirements.txt # Install dependencies
This includes steps like:
- Activating your Google Cloud account
- Installing gcloud
- Creating a project, billing, and enabling APIs
- Creating and assigning roles to a service account
- Setting up Workload Identity Federation
- Generating the WIF key JSON file
- Updating the Python config class
All the steps are documented here
flask run
The backend will start on: http://127.0.0.1:5000
See Cloud Run Deployment section to:
- Build and push Docker image
- Deploy to Cloud Run
- Retrieve the public service URL
cd ../frontend
npm install
Edit the baseURL
in the frontend config file frontend/src/services/api-client.ts
export const baseURL = "http://127.0.0.1:5000"; # local
// or use Cloud Run URL if deploying
npm run dev
Your app should now be accessible on http://localhost:5173
To run the backend on Cloud Run, you’ll first need to build a Docker image of the backend, push it to Docker Hub, and then use the image URL to deploy a Cloud Run service.
docker build -t anthonyiheonye/morph_and_split-backend:12 .
❯ docker push anthonyiheonye/morph_and_split-backend:12
Let’s say we want to deploy a service called morph-and-split-backend
, on Google Cloud Run, using a container image hosted on Docker Hub. To achieve this goal on the command line, we will provide parameters ranging from the Project name to the environment variable for the service account key.
The example below are parameters I used for creating such a service:
- Project:
morph-and-split-toolkit
- Service name:
morph-and-split-backend
- Container image:
docker.io/anthonyiheonye/morph_and_split-backend:12
- Service account :
morph-and-split-toolkit-sa
, which will be used for authentication and permissions - Region:
us-central1
(where the service will be deployed) - Port:
5000
, the port on which the app inside the container listens - Resources: 32 GiB of memory, 8 CPUs, and CPU boost enabled for faster startup
- Scaling: Minimum 1 instance, up to 30 instances, with up to 1000 concurrent requests per instance
- Timeout: 30 minutes (1800 seconds) for long-running tasks
- Secrets: The secret
GCS_SIGNED_URL_KEY
is injected into the container as an environment variable - Access:
--allow-unauthenticated
means the service is publicly accessible - Quiet Mode
--quiet
disables prompts during deployment for smoother automation
gcloud run deploy morph-and-split-backend \
--project=morph-and-split-toolkit \
--image=docker.io/anthonyiheonye/morph_and_split-backend:12 \
--service-account=morph-and-split-toolkit-sa@morph-and-split-toolkit.iam.gserviceaccount.com \
--region=us-central1 \
--port=5000 \
--memory=32Gi \
--cpu=8 \
--timeout=1800 \
--concurrency=1000 \
--min-instances=1 \
--max-instances=30 \
--cpu-boost \
--execution-environment=gen1 \
--set-secrets=GCS_SIGNED_URL_KEY=GCS_SIGNED_URL_KEY:latest \
--allow-unauthenticated \
--quiet
Flag | Purpose |
---|---|
--project |
Your GCP project name |
--image |
Docker container image from Docker Hub |
--service-account |
Cloud Run will use this SA to run the container |
--port |
Port your app listens on inside the container |
--memory |
Allocates 32GiB RAM per container instance |
--cpu |
Allocates 8 vCPUs per instance |
--timeout |
Sets request timeout to 1800 seconds |
--concurrency |
Allows up to 1000 concurrent requests per instance |
--min-instances / --max-instances |
Controls autoscaling range |
--cpu-boost |
Ensures startup CPU boost is enabled |
--execution-environment=gen1 |
Default execution environment (or use gen2 if preferred) |
--set-secrets |
Injects Secret Manager secret into environment variable |
--allow-unauthenticated |
Makes the service public (remove if not needed) |
--quiet |
Suppresses prompts |
After deployment, you will receive an output like this:
Deploying container to Cloud Run service [morph-and-split-backend] in project [morph-and-split-toolkit] region [us-central1]
✓ Deploying...
✓ Creating Revision...
Service [morph-and-split-backend] revision [morph-and-split-backend-xxxxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://morph-and-split-backend-xxxxx-uc.a.run.app
Copy the service URL (the last line), we will use it shortly. However, before we need to set the Cloud Run runtime service account to impersonate the Target account.
The example above, the Cloud Run Runtime service account is the account (--service-account
) we used when setting up the service morph-and-split-backend
:
gcloud run deploy morph-and-split-backend \
--project=morph-and-split-toolkit \
--image=docker.io/anthonyiheonye/morph_and_split-backend:12 \
--service-account=morph-and-split-toolkit-sa@morph-and-split-toolkit.iam.gserviceaccount.com \
While the target impersonated service account
is the account our code is trying to impersonate at runtime. To run the Morph and Split app, this target account should have the ability to tell (impersonate) the Cloud Run Service Account
"Hi friend, it is time to run the service morph-and-split-backend
. I can run it on your behalf".
Importantly, the target account can only communicate with the Cloud Run Service account after we have granted the target account the serviceAccountTokenCreator
role that enables it to impersonate the Cloud Run Service Account.
In the example above, I set the Cloud Run Service account to be same as the Target Impersonated Service Account. To enable communication between both account, let us grant target service account the serviceAccountTokenCreator
role:
gcloud iam service-accounts add-iam-policy-binding morph-and-split-toolkit-sa@morph-and-split-toolkit.iam.gserviceaccount.com \
--member="serviceAccount:morph-and-split-toolkit-sa@morph-and-split-toolkit.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountTokenCreator"
The first service account in the command is the Cloud run Service Account. The next account is the Target impersonated service account. Finally, let's set the base URL to the service URL we copied previouly.
Navigate to:
frontend/src/services/api-client.ts
And update the baseUrl variable with your new Cloud Run URL. After saving the file, your frontend will begin communicating directly with your deployed Cloud Run backend. If it doesn't connect, please revisit the Cloud Run Deployment section to ensure all steps were followed correctly.
You’re now ready to run Morph and Split locally or via Cloud Run. Happy Augmenting!
This project is licensed under the MIT License.
Developed by Anthony Iheonye | LinkedIn