├── requirements.txt ├── Sample Images for Testing ├── meadow-5648849_1280.jpg ├── nature-6722031_1280.jpg ├── mountains-7662717_1280.jpg ├── mountains-8292685_1280.jpg └── ai-generated-9025430_1280.jpg ├── README.md ├── Contribution Guide └── contribution.md └── DeFogify_Main.py /requirements.txt: -------------------------------------------------------------------------------- 1 | opencv-python 2 | gradio 3 | numpy 4 | -------------------------------------------------------------------------------- /Sample Images for Testing/meadow-5648849_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitgoap/DeFogify/HEAD/Sample Images for Testing/meadow-5648849_1280.jpg -------------------------------------------------------------------------------- /Sample Images for Testing/nature-6722031_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitgoap/DeFogify/HEAD/Sample Images for Testing/nature-6722031_1280.jpg -------------------------------------------------------------------------------- /Sample Images for Testing/mountains-7662717_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitgoap/DeFogify/HEAD/Sample Images for Testing/mountains-7662717_1280.jpg -------------------------------------------------------------------------------- /Sample Images for Testing/mountains-8292685_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitgoap/DeFogify/HEAD/Sample Images for Testing/mountains-8292685_1280.jpg -------------------------------------------------------------------------------- /Sample Images for Testing/ai-generated-9025430_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitgoap/DeFogify/HEAD/Sample Images for Testing/ai-generated-9025430_1280.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | This project implements a single-image haze removal technique using the Dark Channel Prior, as described in the research paper: ``Single Image Haze Removal Using Dark Channel Prior`` 4 | The dehazing algorithm is designed to enhance the quality of images affected by haze. This method is effective for various applications in image processing where haze reduction is required. 5 | 6 | ## [Research Paper](https://ieeexplore.ieee.org/document/5567108) 7 | 8 | ## Introduction 9 | 10 | Haze removal is essential for enhancing image clarity and visibility in outdoor scenes, improving the performance of vision-based applications. 11 | 12 | ## Features 13 | 14 | - **Single Image Dehazing** 15 | Process one image at a time to remove haze and bring out hidden details using the Dark Channel Prior. 16 | - **Bulk Image Dehazing** 17 | Process multiple images simultaneously. This feature is perfect for efficiently dehazing an entire batch of images. 18 | - **Video Dehazing** 19 | Dehaze video files by processing each frame individually to ensure a clear and detailed output even for moving visual content. 20 | 21 | 22 | 23 | 24 | 25 | ### Before and After Examples 26 | 27 | | Original Image | Dehazed Image | 28 | |----------------|---------------| 29 | | | | 30 | | | | 31 | | | | 32 | 33 | ## Live Demo 34 | 35 | Try the dehazing app live at ``Hugging Face``: 36 | 37 | [Visit DeFogify in 🤗 space](https://huggingface.co/spaces/MLap/deFogify) 38 | 39 | ## Installation 40 | 41 | Ensure you have the required Python packages installed. Dependencies are listed in the `requirements.txt` file. 42 | 43 | To install the required packages, use: 44 | 45 | ```bash 46 | pip install -r requirements.txt 47 | ``` 48 | 49 | For Ubuntu/Debian-based systems, also install: 50 | 51 | ```bash 52 | sudo apt-get update 53 | sudo apt-get install libgl1-mesa-glx 54 | ``` 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Contribution Guide/contribution.md: -------------------------------------------------------------------------------- 1 | # Contributing To DeFogify 2 | 3 | This documentation guides you in the contribution process. 4 | 5 | ## Submitting Contributions👩‍💻 6 | 7 | 8 | ### 1️⃣ Fork the project 9 | 10 | [Fork](https://github.com/gitgoap/DeFogify/fork) the repository, this creates a copy of the repository under your GitHub profile for working: 11 | ```bash 12 | # clone your fork locally 13 | git clone https://github.com//DeFogify 14 | cd DeFogify 15 | 16 | # add the original repository as 'upstream' remote 17 | git remote add upstream https://github.com/original-owner/DeFogify 18 | ``` 19 | 20 | If you have already forked the project, just update your fork: 21 | ```bash 22 | # Fetch and merge the latest updates from upstream 23 | git pull upstream main 24 | git checkout main 25 | git merge upstream/main 26 | git push origin main 27 | ``` 28 | 29 | Create a new branch before working on a new issue: 30 | ```bash 31 | git checkoout -b 32 | ``` 33 | 34 | ### 2️⃣ Work on feature/fixes 35 | 36 | - Follow the [Installation instruction](https://github.com/gitgoap/DeFogify#installation) to set up your environment. 37 | - Implement the features/fixes. 38 | - After you are done making changes, add the files to git: 39 | ```bash 40 | # To add all new files to the working branch. 41 | git add . 42 | # To add files selectively to the working branch 43 | git file1 file2 file3 44 | ``` 45 | 46 | ### 3️⃣ Commit 47 | 48 | commit the changes with: 49 | ```bash 50 | # This message gets associated with every file in your commit 51 | git commit -m "descriptive message" 52 | ``` 53 | Add a descriptive message for convenience of the reviewer. 54 | 55 | **Note:** Squash multiple commits into one for a clean pull request. 56 | 57 | ### 4️⃣ Push Changes 58 | 59 | Upload your changes to your fork from local: 60 | ```bash 61 | git push -u origin branch_name 62 | ``` 63 | 64 | ### 5️⃣ Pull Request 65 | 66 | - Create a Pull Request from your fork 67 | `Contribute` -> `Open Pull Request` 68 | which will be reviewed and suggestions for improvements. 69 | - Provide a clear title and description linking the associated issue, helps reviewers know context of the Pull Request. 70 | 71 | > Your Pull Request will be reviewed and merged by the maintainer 🚀 72 | 73 | ## Need Help?🤔 74 | You can refer to the following articles on the basics of Git and GitHub and also contact the Project Mentors, in case you are stuck: 75 | 76 | - [Watch this video to get started, if you have no clue about open source](https://youtu.be/SYtPC9tHYyQ) 77 | - [Forking a Repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) 78 | - [Cloning a Repo](https://help.github.com/en/desktop/contributing-to-projects/creating-a-pull-request) 79 | - [How to create a Pull Request](https://opensource.com/article/19/7/create-pull-request-github) 80 | - [Getting started with Git and GitHub](https://towardsdatascience.com/getting-started-with-git-and-github-6fcd0f2d4ac6) 81 | - [GitHub Desktop]([Getting started with GitHub Desktop - GitHub Docs](https://docs.github.com/en/desktop/overview/getting-started-with-github-desktop)) 82 | - [Learn GitHub from Scratch](https://lab.github.com/githubtraining/introduction-to-github) 83 | 84 | Hope you will learn something new while contributing to this project!! 85 | 86 | 87 | -------------------------------------------------------------------------------- /DeFogify_Main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import gradio as gr 4 | import tempfile 5 | import os 6 | from tqdm import tqdm 7 | 8 | # Original Functions 9 | def dark_channel(img, size=15): 10 | r, g, b = cv2.split(img) 11 | min_img = cv2.min(r, cv2.min(g, b)) 12 | kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size)) 13 | dc_img = cv2.erode(min_img, kernel) 14 | return dc_img 15 | 16 | def get_atmo(img, percent=0.001): 17 | mean_perpix = np.mean(img, axis=2).reshape(-1) 18 | mean_topper = mean_perpix[:int(img.shape[0] * img.shape[1] * percent)] 19 | return np.mean(mean_topper) 20 | 21 | def get_trans(img, atom, w=0.95): 22 | x = img / atom 23 | t = 1 - w * dark_channel(x, 15) 24 | return t 25 | 26 | def guided_filter(p, i, r, e): 27 | mean_I = cv2.boxFilter(i, cv2.CV_64F, (r, r)) 28 | mean_p = cv2.boxFilter(p, cv2.CV_64F, (r, r)) 29 | corr_I = cv2.boxFilter(i * i, cv2.CV_64F, (r, r)) 30 | corr_Ip = cv2.boxFilter(i * p, cv2.CV_64F, (r, r)) 31 | var_I = corr_I - mean_I * mean_I 32 | cov_Ip = corr_Ip - mean_I * mean_p 33 | a = cov_Ip / (var_I + e) 34 | b = mean_p - a * mean_I 35 | mean_a = cv2.boxFilter(a, cv2.CV_64F, (r, r)) 36 | mean_b = cv2.boxFilter(b, cv2.CV_64F, (r, r)) 37 | q = mean_a * i + mean_b 38 | return q 39 | 40 | def dehaze(image): 41 | img = image.astype('float64') / 255 42 | img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY).astype('float64') / 255 43 | atom = get_atmo(img) 44 | trans = get_trans(img, atom) 45 | trans_guided = guided_filter(trans, img_gray, 20, 0.0001) 46 | trans_guided = np.maximum(trans_guided, 0.25) # Ensure trans_guided is not below 0.25 47 | result = np.empty_like(img) 48 | for i in range(3): 49 | result[:, :, i] = (img[:, :, i] - atom) / trans_guided + atom 50 | result = np.clip(result, 0, 1) 51 | return (result * 255).astype(np.uint8) 52 | 53 | # Single Image Processing 54 | def process_single_image(image): 55 | dehazed_img = dehaze(image) 56 | return dehazed_img 57 | 58 | # Batch Processing Function for Multiple Images with Progress Bar 59 | def process_images(files): 60 | temp_dir = tempfile.mkdtemp() 61 | output_files = [] 62 | 63 | for file in tqdm(files, desc="Processing Images"): 64 | img = cv2.imread(file.name) 65 | if img is not None: 66 | dehazed_img = dehaze(img) 67 | output_path = os.path.join(temp_dir, os.path.basename(file.name)) 68 | cv2.imwrite(output_path, dehazed_img) 69 | output_files.append(output_path) 70 | 71 | return output_files 72 | 73 | # Video Dehazing Function with Gradio Progress Bar and Error Handling 74 | def dehaze_video(input_video_path, output_video_path, progress=None): 75 | try: 76 | cap = cv2.VideoCapture(input_video_path) 77 | if not cap.isOpened(): 78 | raise ValueError("Error: Could not open video.") 79 | 80 | fourcc = cv2.VideoWriter_fourcc(*'mp4v') 81 | fps = int(cap.get(cv2.CAP_PROP_FPS)) 82 | frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) 83 | frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) 84 | total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) 85 | 86 | if total_frames <= 0: # Assume a constant count for webcam scenarios 87 | total_frames = 1000 88 | 89 | out = cv2.VideoWriter(output_video_path, fourcc, fps, (frame_width, frame_height)) 90 | frame_count = 0 91 | 92 | if progress is not None: 93 | progress(0, desc="Processing Video", unit="frame") 94 | 95 | while cap.isOpened(): 96 | ret, frame = cap.read() 97 | if not ret: 98 | break 99 | dehazed_frame = dehaze(frame) 100 | out.write(dehazed_frame) 101 | frame_count += 1 102 | 103 | if progress is not None: 104 | progress(frame_count / total_frames) # Ensure progress is within 0-1 range 105 | 106 | cap.release() 107 | out.release() 108 | print(f"\nDehazed video saved to: {output_video_path}") 109 | except Exception as e: 110 | print(f"An error occurred during video processing: {e}") 111 | 112 | # Gradio Video Processing Wrapper 113 | def process_video(file): 114 | input_video_path = file # File is a string representing the path 115 | output_video_path = os.path.join(tempfile.mkdtemp(), "dehazed_video.mp4") 116 | progress = gr.Progress() 117 | dehaze_video(input_video_path, output_video_path, progress) 118 | return output_video_path 119 | 120 | # Real-Time Webcam Processing with Gradio Progress Bar 121 | def dehaze_webcam(progress=gr.Progress()): 122 | try: 123 | cap = cv2.VideoCapture(0) # Capture from the first webcam 124 | if not cap.isOpened(): 125 | raise ValueError("Unable to open webcam") 126 | 127 | frame_count = 0 128 | total_frames = 100 # Arbitrary number for progress bar 129 | progress(0, desc="Processing Webcam Feed", unit="frame") 130 | 131 | while frame_count < total_frames: 132 | ret, frame = cap.read() 133 | if not ret: 134 | break 135 | dehazed_frame = dehaze(frame) 136 | frame_count += 1 137 | progress(frame_count / total_frames) # Ensure progress is within 0-1 range 138 | 139 | cv2.imshow('Dehazed Webcam Feed', dehazed_frame) 140 | if cv2.waitKey(1) & 0xFF == ord('q'): 141 | break 142 | 143 | cap.release() 144 | cv2.destroyAllWindows() 145 | progress(1) # Ensure progress bar reaches 100% 146 | except Exception as e: 147 | print(f"An error occurred during webcam processing: {e}") 148 | 149 | # Gradio Webcam Processing Wrapper 150 | def process_webcam(): 151 | progress = gr.Progress() 152 | dehaze_webcam(progress) 153 | return "Webcam processing completed." 154 | 155 | # Example Images for Testing 156 | example_images = [ 157 | "Sample Images for Testing/ai-generated-9025430_1280.jpg", 158 | "Sample Images for Testing/meadow-5648849_1280.jpg", 159 | "Sample Images for Testing/mountains-7662717_1280.jpg", 160 | "Sample Images for Testing/nature-6722031_1280.jpg" 161 | ] 162 | 163 | example_paths = [] 164 | for i, img_path in enumerate(example_images): 165 | img = cv2.imread(img_path) 166 | save_path = f"example_image_{i+1}.png" 167 | cv2.imwrite(save_path, img) 168 | example_paths.append([save_path]) 169 | 170 | # Gradio Interfaces 171 | PixelDehazer = gr.Interface( 172 | fn=process_single_image, 173 | inputs=gr.Image(type="numpy"), 174 | outputs="image", 175 | examples=example_paths, 176 | cache_examples=False, 177 | description="Upload a single image to remove haze." 178 | ) 179 | 180 | BatchDehazer = gr.Interface( 181 | fn=process_images, 182 | inputs=gr.Files(label="Upload Multiple Images", file_types=["image"]), 183 | outputs=gr.Files(label="Download Dehazed Images"), 184 | description="Upload multiple images to remove haze. Download the processed dehazed images." 185 | ) 186 | 187 | VideoDehazer = gr.Interface( 188 | fn=process_video, 189 | inputs=gr.Video(label="Upload a Video"), 190 | outputs=gr.File(label="Download Dehazed Video"), 191 | description="Upload a video to remove haze. Download the processed dehazed video." 192 | ) 193 | 194 | # Combined Gradio App 195 | app = gr.TabbedInterface( 196 | [PixelDehazer, BatchDehazer, VideoDehazer], 197 | ["Single Image Dehazing", "Batch Image Dehazing", "Video Dehazing"], 198 | title="DeFogify App" 199 | ) 200 | 201 | # Launch the Gradio App 202 | if __name__ == "__main__": 203 | app.launch() 204 | --------------------------------------------------------------------------------