Skip to content

Commit

Permalink
Merge pull request #875 from Aryan-Chharia/imagestitching
Browse files Browse the repository at this point in the history
Image Stitching Project using OpenCV library
  • Loading branch information
Niketkumardheeryan authored Jul 29, 2024
2 parents 2579e65 + 8d55ddd commit 42f539e
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 0 deletions.
Binary file added Image Stitching/Dataset/Glacier/CIMG4017.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Image Stitching/Dataset/Glacier/CIMG4018.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Image Stitching/Dataset/Mountain/hill1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Image Stitching/Dataset/Mountain/hill2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions Image Stitching/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Image Stitching using OpenCV library

## Project Overview

This project demonstrates how to stitch multiple images together using the OpenCV library in Python. It involves loading images from a dataset, stitching them together, and post-processing the stitched image to remove any unwanted borders.

## Process Details
Load Images: Reads all images from the specified dataset folder.
Image Stitching: Uses OpenCV's stitching functionality to combine the images into a panorama.
Post-Processing: Adds a black border to the stitched image, converts it to grayscale, applies a threshold, and finds the largest contour to remove any black borders.
Save Results: Saves the stitched image and the post-processed image in the Result/ directory.

Example:
If you have images in the Dataset/Glacier folder, set dataset_name = 'Glacier' in the script. The resulting images will be saved in the Result/Glacier folder as output1.jpg (stitched image) and output2.jpg (processed image).

## Directory Structure

The project directory is organized as follows:

- `Dataset/`: Contains the input images for different datasets (e.g., Glacier, Mountain).
- `Result/`: Stores the stitched images and the post-processed images for different datasets.
- `stitching.py`: The main script that performs the image stitching and border removal.
- `README.md`: This file, containing the project description and usage instructions.

## Requirements

Make sure you have the following packages installed:

- numpy
- opencv-python
- imutils
- glob
- os

You can install the required packages using the pip command

## Usage

### Dataset Preparation:

Place the images you want to stitch together in a folder under the Dataset/ directory.
Ensure the images are in JPG format. If they are not, you can either convert them to jpg or change the extension, used in the code, to the extension you want.

### Running the Script:

Update the dataset_name variable in stitching.py to the name of the folder containing your images.
Adjust the threshold variable if necessary (default is 0.97).
Run the stitching.py script

## Results:

The stitched image and the post-processed image will be saved in the corresponding folder under the Result/ directory.
The script will also display the images using OpenCV's imshow function.

![My Image](Result/Mountain/output1.jpg)
![My Image](Result/Mountain/output2.jpg)
Binary file added Image Stitching/Result/Glacier/output1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Image Stitching/Result/Glacier/output2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Image Stitching/Result/Mountain/output1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Image Stitching/Result/Mountain/output2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
173 changes: 173 additions & 0 deletions Image Stitching/stitching.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"# import the necessary modules\n",
"import numpy as np\n",
"import cv2\n",
"import glob\n",
"import imutils\n",
"import os"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"# Configurable parameters\n",
"dataset_name = 'Mountain' # change the name according to the folder name in the dataset folder\n",
"threshold = 0.97 \n",
"\n",
"# Define a threshold to determine if the border is small\n",
"# The threshold represents the proportion of the image area that the largest contour should cover.\n",
"# If the area of the largest contour is less than this threshold proportion of the total image area,\n",
"# it indicates that the black border around the stitched image is small enough to be removed."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# Load images\n",
"file_path = os.path.join('Dataset', dataset_name, '*.jpg') # construct the file path where the images are\n",
"image_paths = glob.glob(file_path) # extract the images' path from the file path\n",
"images = []\n",
"# for each image path in image paths, extract the images and append them to the images list\n",
"# if the image is not loaded because of some reason, give an error\n",
"for image_path in image_paths: \n",
" img = cv2.imread(image_path)\n",
" if img is not None:\n",
" images.append(img)\n",
" else:\n",
" print(f\"Error loading image: {image_path}\")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"# Check OpenCV version and create the stitcher accordingly\n",
"if cv2.__version__.startswith('3'):\n",
" stitcher = cv2.createStitcher()\n",
"else:\n",
" stitcher = cv2.Stitcher_create()"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"# Stitch the images and return the stitched image\n",
"error, stitched_img = stitcher.stitch(images)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"# If the image has been successfully stitched, then we proceed futhur else show the error\n",
"# Here cv2.Stitcher_OK checks if the image is stitched properly\n",
"\n",
"if error == cv2.Stitcher_OK:\n",
" # Save the stitched image\n",
" result_folder = os.path.join('Result', dataset_name)\n",
" os.makedirs(result_folder, exist_ok=True) # this checks if directory where we have to store \n",
" # is there or not, else create it\n",
"\n",
" output_path1 = os.path.join(result_folder, 'output1.jpg') # output path\n",
" \n",
" cv2.imwrite(output_path1, stitched_img) # saves the image\n",
" cv2.imshow(\"Stitched Image\", stitched_img) # show the image\n",
" cv2.waitKey(0) # wait till any key is pressed\n",
" cv2.destroyAllWindows() # closes all the windows opened if any key is pressed\n",
"\n",
"# The stitched image often has a black border around it (see the result folder for an example)\n",
"# Hence, we need to remove the black border to get the finished image\n",
"# The following code selects a ROI (region of interest), which is usually a rectangle smaller than the image,\n",
"# and removes the black border by finding and cropping to the largest contour within the thresholded image.\n",
"\n",
"\n",
" # Add a border to the stitched image\n",
" stitched_img = cv2.copyMakeBorder(stitched_img, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=(0, 0, 0))\n",
"\n",
" # Convert to grayscale and apply threshold to find the black border\n",
" gray = cv2.cvtColor(stitched_img, cv2.COLOR_BGR2GRAY)\n",
" _, thresh_img = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)\n",
"\n",
" # Find contours in the thresholded image\n",
" contours = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = imutils.grab_contours(contours)\n",
" areaOI = max(contours, key=cv2.contourArea)\n",
"\n",
" # Define a threshold to determine if the border is small\n",
" if cv2.contourArea(areaOI) < threshold * gray.size:\n",
" # Create a mask with the largest contour\n",
" mask = np.zeros(thresh_img.shape, dtype=\"uint8\")\n",
" x, y, w, h = cv2.boundingRect(areaOI)\n",
" cv2.rectangle(mask, (x, y), (x + w, y + h), 255, -1)\n",
"\n",
" # Erode the mask until the black border is removed\n",
" minRectangle = mask.copy()\n",
" sub = mask.copy()\n",
"\n",
" while cv2.countNonZero(sub) > 0:\n",
" minRectangle = cv2.erode(minRectangle, None)\n",
" sub = cv2.subtract(minRectangle, thresh_img)\n",
"\n",
" # Find the new bounding box\n",
" contours = cv2.findContours(minRectangle, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = imutils.grab_contours(contours)\n",
" areaOI = max(contours, key=cv2.contourArea)\n",
" x, y, w, h = cv2.boundingRect(areaOI)\n",
"\n",
" # Crop the image to the bounding box\n",
" stitched_img = stitched_img[y:y + h, x:x + w]\n",
"\n",
" # Save the processed image\n",
" output_path2 = os.path.join(result_folder, 'output2.jpg')\n",
" cv2.imwrite(output_path2, stitched_img)\n",
" cv2.imshow(\"Stitched Image Processed\", stitched_img)\n",
" cv2.waitKey(0)\n",
" cv2.destroyAllWindows()\n",
"else:\n",
" print(\"Error during stitching:\", error)\n",
" print(\"Images could not be stitched! Likely not enough keypoints being detected!\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

0 comments on commit 42f539e

Please sign in to comment.