File size: 5,494 Bytes
5bab8dc
add89e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fb26f91
add89e8
fb26f91
 
add89e8
 
 
 
 
fb26f91
 
add89e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
efcd4e0
add89e8
2ce50e6
 
 
add89e8
2ce50e6
add89e8
 
fb26f91
add89e8
 
 
 
 
 
 
 
3abfd38
add89e8
 
 
 
27cefb7
c460ff7
add89e8
 
 
27cefb7
 
 
add89e8
 
 
c460ff7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import streamlit as st
import cv2
import numpy as np
import base64
from PIL import Image
import io

def convert_cv2_to_base64(cv2_image):
    """Convert a cv2 image to a base64 string."""
    _, buffer = cv2.imencode('.jpg', cv2_image)
    return base64.b64encode(buffer).decode('utf-8')

def convert_base64_to_cv2(base64_string):
    """Convert base64 string to a cv2 image."""
    try:
        image_bytes = base64.b64decode(base64_string)
        pil_image = Image.open(io.BytesIO(image_bytes))
        cv2_image = np.array(pil_image)
        cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_RGB2BGR)
        return cv2_image
    except Exception as e:
        print(f"Error decoding base64: {e}")
        return None

def extract_numbers(cv2_image):
    """
    Extracts numbers from the image using OpenCV.
    """
    # Preprocessing
    gray = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(equalized, 128, 255, cv2.THRESH_BINARY_INV)[1] #invert

    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    number_contours = []
    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)
        area = cv2.contourArea(contour)
        if 20 < area < 500 and 10 < w < 100 and 10 < h < 100 and 0.1 < w / h < 1.0: #adjusted
            number_contours.append(contour)

    numbers = []
    for i, contour in enumerate(number_contours):
        x, y, w, h = cv2.boundingRect(contour)
        if x > cv2_image.shape[1] / 4 and x < cv2_image.shape[1]/2 and y < cv2_image.shape[0] / 2:
            number = 1
        elif x > cv2_image.shape[1] / 2 and x < cv2_image.shape[1] * 3/4 and y < cv2_image.shape[0]/2:
            number = 2
        elif x > cv2_image.shape[1] / 4 and x < cv2_image.shape[1]/2 and y > cv2_image.shape[0]/2:
            number = 3
        elif x > cv2_image.shape[1] / 2 and x < cv2_image.shape[1] * 3/4 and y > cv2_image.shape[0]/2:
            number = 4
        else:
            number = i + 1
        numbers.append(number)
    return numbers



def find_regions(cv2_image, number):
    """Find pixels connected to the given number."""
    gray = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)[1]
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    region_pixels = []
    min_x, min_y = cv2_image.shape[1], cv2_image.shape[0]
    max_x, max_y = 0, 0
    found_number = False

    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)
        if 10 < w < 50 and 10 < h < 50 and 0.2 < w / h < 1.0:
            extracted_number = i+1
            if extracted_number == number:
                found_number = True
                for i in range(y, y + h):
                    for j in range(x, x + w):
                        if 0 <= i < cv2_image.shape[0] and 0 <= j < cv2_image.shape[1]:
                            region_pixels.append((j, i))
                            min_x = min(min_x, j)
                            min_y = min(min_y, i)
                            max_x = max(max_x, j)
                            maxY = max(max_y, i)
    if not found_number:
        return [], None
    return region_pixels, (min_x,min_y,max_x,max_y)

def process_and_display(image_file):
    """Process the image and display the result."""
    try:
        # Read image file
        image_bytes = image_file.read()
        pil_image = Image.open(io.BytesIO(image_bytes))
        cv2_image = np.array(pil_image)
        cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_RGB2BGR)

        # Process the image with OpenCV
        numbers = extract_numbers(cv2_image) #use the function
        st.session_state.numbers = numbers

        # Display
        image_base64 = convert_cv2_to_base64(cv2_image)
        st.image(f"data:image/jpeg;base64,{image_base64}", caption="Uploaded Image", use_container_width=True)

        # Display the numbers
        st.write("Recognized Numbers:", numbers) # <--- ADD THIS LINE

        # Create number buttons
        cols = st.columns(max(1, len(numbers)))  # Ensure at least 1 column is created
        for i, number in enumerate(numbers):
            with cols[i]:
                if st.button(f"Highlight {number}", key=f"highlight_{number}"): # ADDED KEY
                    st.session_state.selected_number = number
                    region, border = find_regions(cv2_image, number)
                    highlighted_image = cv2_image.copy()
                    if border:
                        cv2.rectangle(highlighted_image, (int(border[0]), int(border[1])), (int(border[2]), int(border[3])), (0, 0, 0), 2)
                    for x, y in region:
                         highlighted_image[y, x] = [255, 255, 0]
                    highlighted_image_base64 = convert_cv2_to_base64(highlighted_image)
                    st.image(f"data:image/jpeg;base64,{highlighted_image_base64}", caption=f"Highlighted {number}", use_container_width=True)
    except Exception as e:
        st.error(f"Error processing image: {e}")

def main():
    st.title("Paint by Numbers Solver")
    # st.set_option('deprecation.showfileuploaderlabel', False) # Remove this line
    image_file = st.file_uploader("Upload a Paint by Numbers image", type=["png", "jpg", "jpeg"])

    if image_file is not None:
        # Pass the image file to process_and_display
        process_and_display(image_file) #  CHANGE HERE
        


if __name__ == "__main__":
    main()