Skip to content

Commit

Permalink
enhance color contrast
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashutosh committed Aug 13, 2024
1 parent e67a4d8 commit d4de08f
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 49 deletions.
124 changes: 93 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
<img src="ss1.png"/>

---
**grayscale contrast**

<img src="ss3.png"/>

**enhance color contrast**
<img src="ss4.png"/>

## ⚙️Tech-Stack
- **Python**
- **OpenCV**
Expand All @@ -23,48 +27,106 @@
- **Pandas**

## 🧤Image Preprocessing Steps
1. **Image Reading**:
Reads the uploaded image file and decodes it into a format that OpenCV can process. This converts the image from a file-like object to a NumPy array.

```python
file_bytes = np.asarray(bytearray(uploaded_image.read()), dtype=np.uint8)
image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
```
### Purpose
The `enhance_color_contrast` function is designed to preprocess an image by enhancing its color and contrast to make text more visible while reducing the effects of bleed-through. After enhancing the image, the function converts it into a black-and-white (binary) format, making it suitable for document analysis, OCR, or similar tasks where clear text visibility is crucial.

### Function Signature
```python
def enhance_color_contrast(uploaded_image):
```

3. **Grayscale Conversion**:
Converts the color image to a grayscale image. This simplifies the image data, making it easier to process for text extraction.
### Parameters
- **`uploaded_image`**:
- **Type**: File-like object (e.g., an uploaded image file)
- **Description**: This is the image file provided by the user. It is expected to be in a readable format like JPEG, PNG, etc.

### Returns
- **`bw_image`**:
- **Type**: PIL Image object
- **Description**: A black-and-white version of the processed image where the text is more visible, and the effects of bleed-through are minimized.

### Step-by-Step Processing

1. **Read the Image**
```python
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
```
file_bytes = np.asarray(bytearray(uploaded_image.read()), dtype=np.uint8)
image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
```
- **Description**: Converts the uploaded file into an OpenCV-readable format. The image is decoded into a BGR color format.

4. **Bilateral Filtering**:
An image processing technique that smooths an image while preserving edges. It averages the colors of nearby pixels, but only considers those that are similar in color, effectively reducing noise while maintaining important details.
2. **Check Image Validity**
```python
if image is None:
raise ValueError("Error: Unable to read the image. Please upload a valid image file.")
```
- **Description**: Ensures that the image was loaded correctly. If the image is invalid, an error is raised.

3. **Convert Image to PIL Format**
```python
filtered = cv2.bilateralFilter(gray, d=9, sigmaColor=75, sigmaSpace=75)
```
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
```
- **Description**: Converts the image from OpenCV’s BGR format to RGB and then to a PIL Image object for easier manipulation.

5. **Adaptive Thresholding**:
A method used to create a binary image based on local pixel intensity. It analyzes the neighborhood of each pixel and dynamically determines the threshold value, ensuring that text in bright areas stands out against a darker background and vice versa.
4. **Enhance Contrast**
```python
contrast_enhancer = ImageEnhance.Contrast(pil_image)
pil_image = contrast_enhancer.enhance(1.5)
```
- **Description**: Increases the contrast of the image by a factor of 1.5. This step makes the text stand out more against the background.

5. **Enhance Color Saturation**
```python
adaptive_thresh = cv2.adaptiveThreshold(filtered, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
```
color_enhancer = ImageEnhance.Color(pil_image)
pil_image = color_enhancer.enhance(1.5)
```
- **Description**: Enhances the color saturation by a factor of 1.5. This makes the colors in the image more vivid, which can further improve text visibility.

6. **Morphological Operations**:
A set of image processing techniques that manipulate the structure of an image. They involve applying techniques like dilation and erosion to enhance or suppress certain features. **Dilation** adds pixels to the boundaries of objects, while **erosion** removes pixels from the edges. These operations help in connecting gaps, smoothing irregularities, and refining the appearance of text for better extraction.
6. **Convert Back to OpenCV Format**
```python
enhanced_image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
```
- **Description**: Converts the enhanced image back to the BGR format for further processing in OpenCV.

7. **Apply Optional Denoising**
```python
kernel = np.ones((3, 3), np.uint8)
morph = cv2.morphologyEx(adaptive_thresh, cv2.MORPH_CLOSE, kernel)
```

7. **Conversion to PIL Image**:
Converts the processed NumPy array (binary image) back into a PIL Image object. This format is often required for further processing or display in applications like Streamlit.

denoised_image = cv2.fastNlMeansDenoisingColored(enhanced_image, None, 10, 10, 7, 21)
```
- **Description**: Applies mild denoising to reduce any remaining noise in the image, which may have been intensified by the previous enhancements.

8. **Convert Back to PIL Format**
```python
final_pil_image = Image.fromarray(denoised_image)
```
- **Description**: Converts the denoised image back to a PIL Image object for final processing.

9. **Convert to Grayscale**
```python
pil_image = Image.fromarray(morph)
```

These preprocessing steps collectively enhance the visibility of text in images while reducing noise and interference from other elements, making it easier for text extraction models to accurately read the text.
grayscale_image = final_pil_image.convert('L')
```
- **Description**: Converts the enhanced image to grayscale. This step prepares the image for binarization.

10. **Apply Binary Threshold for Black and White Conversion**
```python
bw_image = grayscale_image.point(lambda x: 0 if x < 128 else 255, '1')
```
- **Description**: Converts the grayscale image into a black-and-white (binary) image using a threshold value of 128. Pixels with a value below 128 are set to black, and pixels with a value of 128 or higher are set to white.

11. **Return the Final Image**
```python
return bw_image
```
- **Description**: The final black-and-white image is returned, with enhanced text visibility and reduced bleed-through.

### Example Usage
```python
# Assuming 'uploaded_file' is an image file object obtained from a file upload
processed_image = enhance_color_contrast(uploaded_file)
processed_image.show() # Displays the processed black-and-white image
```

### Notes
- **Adjustable Parameters**: The contrast and color saturation factors can be adjusted to better suit the specific characteristics of the image. The default factors are set to 1.5, but they can be increased or decreased depending on the desired level of enhancement.
- **Denoising**: The denoising step is optional and can be adjusted or removed if the image is already clean or if noise reduction is not needed.

This function is particularly useful for preprocessing scanned documents, where text needs to be **clearly visible**, and **bleed-through** from the other side of the page needs to be minimized.
22 changes: 9 additions & 13 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import streamlit as st
from PIL import Image
from preprocess_image import grayscale_contrast
from preprocess_image import grayscale_contrast, adobe_like_preprocessing, enhance_color_contrast
import io

st.markdown("<center><h1>📜📄 Document Extraction</h1></center>",
unsafe_allow_html=True)

# Display logo in the sidebar
st.sidebar.image("logo.jpg", use_column_width=True)

# Image uploader in the sidebar
uploaded_image = st.sidebar.file_uploader("**Upload Image**",
type=["jpg", "jpeg", "png"])

if uploaded_image:
# Process the uploaded image
preprocessed_image = grayscale_contrast(uploaded_image)
#preprocessed_image = grayscale_contrast(uploaded_image)
# preprocessed_image = adobe_like_preprocessing(uploaded_image)
preprocessed_image = enhance_color_contrast(uploaded_image)

# Create two columns for side-by-side display
col1, col2 = st.columns(2)

with col1:
Expand All @@ -32,23 +31,20 @@
caption="Processed Image",
use_column_width=True)

# Convert the preprocessed image to bytes for downloading
img_byte_arr = io.BytesIO()
preprocessed_image.save(img_byte_arr, format='PNG')
img_byte_arr.seek(0)

# Add a download button
st.download_button(
label="Download Processed Image",
data=img_byte_arr,
file_name="processed_image.png",
mime="image/png"
)
st.download_button(label="Download Processed Image",
data=img_byte_arr,
file_name="processed_image.png",
mime="image/png")

# Display success message
st.success("Image processed successfully!")
else:
st.info("Please upload an image to see the results.")

# Feedback and suggestions section in the sidebar
st.sidebar.info("[Feedback & Suggestions](mailto:subrata@thealgohype.com)")
st.sidebar.info("[Feedback & Suggestions](mailto:subrata@thealgohype.com)")
88 changes: 83 additions & 5 deletions preprocess_image.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import cv2
import numpy as np
from PIL import Image
from PIL import Image, ImageEnhance
import io


def grayscale_contrast(uploaded_image):
"""Returns a PIL Image object of the preprocessed image"""
# Read the image from the uploaded file-like object
Expand All @@ -22,9 +23,9 @@ def grayscale_contrast(uploaded_image):
filtered = cv2.bilateralFilter(gray, d=9, sigmaColor=75, sigmaSpace=75)

# Apply adaptive thresholding
adaptive_thresh = cv2.adaptiveThreshold(
filtered, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
)
adaptive_thresh = cv2.adaptiveThreshold(filtered, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)

# Optional: Morphological operations to enhance text
kernel = np.ones((3, 3), np.uint8)
Expand All @@ -33,4 +34,81 @@ def grayscale_contrast(uploaded_image):
# Convert the preprocessed image to a PIL Image object
pil_image = Image.fromarray(morph)

return pil_image
return pil_image


def adobe_like_preprocessing(uploaded_image):
"""Applies preprocessing to enhance text visibility while minimizing bleed-through."""
# Read the image from the uploaded file-like object
file_bytes = np.asarray(bytearray(uploaded_image.read()), dtype=np.uint8)
image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)

# Check if the image was loaded successfully
if image is None:
raise ValueError(
"Error: Unable to read the image. Please upload a valid image file."
)

# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Apply Gaussian blur with a smaller kernel to reduce bleed-through but preserve text
blurred = cv2.GaussianBlur(gray, (3, 3), 0)

# Apply adaptive thresholding with slightly stricter settings to retain text clarity
adaptive_thresh = cv2.adaptiveThreshold(blurred, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 25, 8)

# Apply a morphological operation to clean up small dots and noise
kernel = np.ones((2, 2), np.uint8)
cleaned = cv2.morphologyEx(adaptive_thresh, cv2.MORPH_OPEN, kernel)

# Convert the preprocessed image to a PIL Image object
pil_image = Image.fromarray(cleaned)

return pil_image


def enhance_color_contrast(uploaded_image):
"""Enhances color and contrast to make text more visible and reduce bleed-through, then converts to black and white."""
# Read the image from the uploaded file-like object
file_bytes = np.asarray(bytearray(uploaded_image.read()), dtype=np.uint8)
image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)

# Check if the image was loaded successfully
if image is None:
raise ValueError(
"Error: Unable to read the image. Please upload a valid image file."
)

# Convert the image to PIL for easy manipulation
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

# Enhance the contrast
contrast_enhancer = ImageEnhance.Contrast(pil_image)
pil_image = contrast_enhancer.enhance(
1.5) # Increase contrast (adjust the factor as needed)

# Enhance the color saturation
color_enhancer = ImageEnhance.Color(pil_image)
pil_image = color_enhancer.enhance(
1.5) # Increase saturation (adjust the factor as needed)

# Convert back to OpenCV format
enhanced_image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)

# Optional: Apply mild denoising to reduce any remaining noise
denoised_image = cv2.fastNlMeansDenoisingColored(enhanced_image, None, 10,
10, 7, 21)

# Convert the denoised image back to a PIL Image object for final processing
final_pil_image = Image.fromarray(denoised_image)

# Convert the image to grayscale
grayscale_image = final_pil_image.convert('L')

# Apply a binary threshold to convert the image to black and white
bw_image = grayscale_image.point(lambda x: 0 if x < 128 else 255, '1')

return bw_image
Binary file added ss4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d4de08f

Please sign in to comment.