Skip to content

Commit 52a0ec4

Browse files
committed
added code for pipeline-1
0 parents  commit 52a0ec4

File tree

3 files changed

+387
-0
lines changed

3 files changed

+387
-0
lines changed

pipeline-1/capture_img.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""
2+
This is a python script to capture images from the camera
3+
and save them to the local directory in an interval of every 5 seconds.
4+
"""
5+
6+
import cv2
7+
import time
8+
from datetime import datetime
9+
import os
10+
import logging
11+
12+
13+
def create_dir(**kwargs) -> None:
14+
"""
15+
This function creates a new directory if it does not exist
16+
the name of directory is images
17+
"""
18+
import os
19+
20+
if kwargs is not None and "path" in kwargs:
21+
path = kwargs["path"]
22+
else:
23+
path = "images"
24+
25+
format = "%(asctime)s [%(levelname)s] %(message)s"
26+
logging.basicConfig(
27+
level=logging.INFO,
28+
format=format,
29+
handlers=[logging.FileHandler("pipeline-1-capture-img.log")],
30+
)
31+
32+
if not os.path.exists(path):
33+
try:
34+
os.mkdir(path)
35+
logging.log(level=logging.INFO, msg=f"Directory {path} Created")
36+
except Exception as e:
37+
logging.log(level=logging.ERROR, msg=e)
38+
logging.log(level=logging.ERROR, msg=f"Unable to create directory {path}")
39+
else:
40+
logging.log(level=logging.INFO, msg=f"{path} directory already exists")
41+
42+
43+
def capture_img(**kwargs) -> None:
44+
"""
45+
This function captures images from the camera and saves them to the local directory
46+
in an interval of every 5 seconds.
47+
"""
48+
49+
cap = cv2.VideoCapture(0)
50+
51+
if not cap.isOpened():
52+
logging.log(level=logging.ERROR, msg="Unable to read camera feed")
53+
54+
# Default resolutions of the frame are obtained.The default resolutions are system dependent.
55+
# We convert the resolutions from float to integer.
56+
frame_width = int(cap.get(3))
57+
frame_height = int(cap.get(4))
58+
59+
if not os.path.exists(os.path.join(os.getcwd(), "images")):
60+
logging.log(
61+
level=logging.FATAL,
62+
msg="images directory not found, restart the script to create the directory",
63+
)
64+
exit(1)
65+
else:
66+
logging.log(level=logging.INFO, msg="images directory found")
67+
68+
while True:
69+
ret, frame = cap.read()
70+
71+
if ret == True:
72+
timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
73+
filename = f"{os.getcwd()}/images/img_{timestamp}.jpg"
74+
75+
cv2.imwrite(filename, frame)
76+
time.sleep(0.2)
77+
78+
if kwargs is not None and "queue" in kwargs:
79+
queue = kwargs["queue"]
80+
queue.put(filename)
81+
82+
logging.log(
83+
level=logging.INFO,
84+
msg=f"Image saved named img_{timestamp}.jpg in the directory images",
85+
)
86+
87+
# Press Q on keyboard to stop recording
88+
if cv2.waitKey(1) & 0xFF == ord("q"):
89+
break
90+
91+
time.sleep(5)
92+
93+
else:
94+
break
95+
96+
cap.release()
97+
cv2.destroyAllWindows()
98+
99+
100+
if __name__ == "__main__":
101+
format = "%(asctime)s [%(levelname)s] %(message)s"
102+
logging.basicConfig(
103+
level=logging.INFO,
104+
format=format,
105+
handlers=[logging.FileHandler("pipeline-1.log")],
106+
)
107+
108+
logging.log(level=logging.INFO, msg="Starting the pipeline")
109+
pipeline = (create_dir, capture_img)
110+
111+
for func in pipeline:
112+
func()

pipeline-1/pipeline-1-main.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Imports
2+
import capture_img
3+
from segmentation2polygon import segmentation2polygon, do_perspective_transformation
4+
import queue
5+
import logging
6+
import threading
7+
import cv2
8+
from time import perf_counter, sleep
9+
import os
10+
11+
"""
12+
TODO:
13+
- we have to add two options
14+
[] - if only one image is given
15+
[done] - if we have to do it using feed from webcam
16+
- the pipeline gets stopped if we get more number of points after segmentation add that.
17+
18+
"""
19+
20+
21+
def initialize() -> None:
22+
"""
23+
This function initializes the constant factors of the pipeline like the yolo model, logging, and to create a directory to store the images
24+
"""
25+
format = "%(asctime)s [%(levelname)s] %(message)s"
26+
logging.basicConfig(
27+
level=logging.DEBUG,
28+
format=format,
29+
handlers=[logging.FileHandler("pipeline-1.log")],
30+
)
31+
32+
from ultralytics import YOLO
33+
34+
logging.log(logging.INFO, "Loading the YOLO model")
35+
global model
36+
model = YOLO("./model_yolo/best.pt")
37+
logging.log(logging.INFO, "Loaded the YOLO model")
38+
39+
logging.log(level=logging.INFO, msg="Creating the directory structure!")
40+
if not os.path.exists("images"):
41+
capture_img.create_dir(path="images")
42+
if not os.path.exists("processed_images"):
43+
capture_img.create_dir(path="processed_images")
44+
logging.log(level=logging.DEBUG, msg=f"{os.getcwd()}")
45+
logging.log(level=logging.INFO, msg="Created the directory structure!")
46+
47+
sleep(2)
48+
49+
50+
def get_segmentation(img_path: str):
51+
"""
52+
This function returns the segmentation mask of the image
53+
Input: img_path: str
54+
Output: segmentation: np.ndarray
55+
"""
56+
logging.log(logging.INFO, "Getting the segmentation mask of the image")
57+
start = perf_counter()
58+
results = model.predict(img_path)
59+
end = perf_counter()
60+
logging.log(
61+
logging.INFO,
62+
f"Time taken to get the segmentation mask of the image: {end-start}",
63+
)
64+
65+
try:
66+
mask = results[0].masks
67+
mask = mask.xy
68+
segmentation = segmentation2polygon(mask[0])
69+
print(segmentation.reshape((-1, 2)))
70+
return segmentation.reshape((-1, 2))
71+
except Exception as e:
72+
logging.log(logging.ERROR, e)
73+
return None
74+
75+
76+
def main() -> None:
77+
img_queue = queue.Queue()
78+
79+
img_capture_thread = threading.Thread(
80+
target=capture_img.capture_img, kwargs={"queue": img_queue}, daemon=True
81+
)
82+
img_capture_thread.start()
83+
84+
while True:
85+
if 0xFF == ord("q"):
86+
break
87+
88+
if not img_queue.empty():
89+
img_path = img_queue.get()
90+
logging.log(level=logging.DEBUG, msg=f"{img_queue.qsize()}")
91+
logging.log(level=logging.DEBUG, msg=f"Got the image path: {img_path}")
92+
segmentation = get_segmentation(img_path=img_path)
93+
logging.log(
94+
level=logging.INFO, msg="Got the segmentation mask of the image"
95+
)
96+
97+
if segmentation is not None:
98+
img = cv2.imread(filename=img_path)
99+
processed_image = do_perspective_transformation(
100+
image=img, input_array=segmentation
101+
)
102+
logging.log(level=logging.INFO, msg="Got the processed image")
103+
104+
try:
105+
if not os.path.exists("processed_images"):
106+
logging.log(
107+
level=logging.INFO,
108+
msg="Creating the directory to store the processed images",
109+
)
110+
os.mkdir("processed_images")
111+
img_name = img_path.split("/")[-1]
112+
cv2.imwrite(
113+
filename=f"processed_images/{img_name}", img=processed_image
114+
)
115+
logging.log(level=logging.INFO, msg="Saved the processed image")
116+
117+
except Exception as e:
118+
logging.log(level=logging.ERROR, msg=e)
119+
logging.log(
120+
level=logging.ERROR, msg="Unable to save the processed image"
121+
)
122+
img_queue.task_done()
123+
124+
img_capture_thread.join()
125+
return
126+
127+
128+
if __name__ == "__main__":
129+
initialize()
130+
main()
131+
del model
132+
exit(0)

pipeline-1/segmentation2polygon.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Imports
2+
import numpy as np
3+
import cv2
4+
5+
6+
# Functions
7+
def get_segmentation():
8+
"""
9+
HELPER FUNCTION
10+
This function returns the segmentation mask of the image
11+
"""
12+
return "0 0.55 0.255556 0.548437 0.258333 0.521875 0.258333 0.520312 0.261111 0.515625 0.261111 0.514063 0.263889 0.50625 0.263889 0.504687 0.266667 0.496875 0.266667 0.495313 0.269444 0.489062 0.269444 0.4875 0.272222 0.476562 0.272222 0.475 0.275 0.448438 0.275 0.446875 0.277778 0.432813 0.277778 0.43125 0.280556 0.43125 0.705556 0.432813 0.708333 0.50625 0.708333 0.507812 0.705556 0.51875 0.705556 0.520312 0.702778 0.529688 0.702778 0.53125 0.7 0.546875 0.7 0.548437 0.702778 0.69375 0.702778 0.695312 0.705556 0.715625 0.705556 0.717188 0.708333 0.734375 0.708333 0.735937 0.705556 0.7375 0.705556 0.7375 0.7 0.739062 0.697222 0.739062 0.680556 0.740625 0.677778 0.740625 0.616667 0.742188 0.613889 0.742188 0.258333 0.740625 0.255556"
13+
14+
15+
def convert_str_to_array(string) -> list[float]:
16+
"""
17+
HELPER FUNCTION
18+
This function converts the string to a list of floats
19+
Input: string: string of points
20+
Output: list of floats
21+
"""
22+
return [float(i) for i in string.split(" ")]
23+
24+
25+
def get_height_and_width_of_img(img_path) -> dict[str, int]:
26+
"""
27+
HELPER FUNCTION
28+
This function returns the height and width of the image
29+
Input: img_path: path to the image
30+
Output: dict of height and width
31+
"""
32+
img = cv2.imread(img_path)
33+
h, w, c = img.shape
34+
return {"height": h, "width": w}
35+
36+
37+
def from_normalised_to_pixel(img_size: dict, contour):
38+
"""
39+
function converts the normalised segmentation mask to pixel values
40+
Input: img_size: dict of height and width of image
41+
contour: list of points
42+
Output: res: list of points
43+
"""
44+
height = img_size["height"]
45+
width = img_size["width"]
46+
res = []
47+
48+
for i in range(len(contour[1::2])):
49+
x = int(contour[2 * i + 1] * width)
50+
y = int(contour[2 * i + 2] * height)
51+
res.append((x, y))
52+
53+
return res
54+
55+
56+
def segmentation2polygon(segmentation):
57+
"""
58+
function converts the segmentation mask from yolo V8 to a 4 sided polygon
59+
Input: segmentation: list of points
60+
Output: approx: list of points
61+
"""
62+
63+
points = np.array(segmentation, dtype=np.int32)
64+
65+
closed = True
66+
67+
peri = cv2.arcLength(points, True)
68+
approx = cv2.approxPolyDP(points, 0.02 * peri, closed)
69+
70+
return approx
71+
72+
73+
def pipeline():
74+
"""
75+
Function is the pipeline for the segmentation to polygon conversion
76+
"""
77+
segmentation = get_segmentation()
78+
segmentation = convert_str_to_array(segmentation)
79+
img_size = get_height_and_width_of_img(
80+
"./yolo/yolo_predictions/medicakolkata_rcu_mon--8_2022_5_18_17_5_38.jpeg"
81+
)
82+
polygon = from_normalised_to_pixel(img_size, segmentation)
83+
polygon = segmentation2polygon(polygon)
84+
85+
return polygon
86+
87+
88+
def draw_points(polygon) -> None:
89+
"""
90+
Function to draw the points on the image, it shows the image in the new window with points
91+
Input: polygon: list of points
92+
Output: None
93+
"""
94+
img = cv2.imread(
95+
"./yolo/yolo_predictions/medicakolkata_rcu_mon--8_2022_5_18_17_5_38.jpeg"
96+
)
97+
for i in polygon:
98+
cv2.circle(img, (i[0], i[1]), 5, (0, 0, 255), -1)
99+
100+
cv2.imshow("img", img)
101+
cv2.waitKey(0)
102+
cv2.destroyAllWindows()
103+
104+
105+
def do_perspective_transformation(image, input_array):
106+
"""
107+
Function to do perspective transformation
108+
Input: image: image on which perspective transformation is to be done
109+
input_array: list of points
110+
Output: result: image after perspective transformation
111+
"""
112+
height, width = image.shape[:2]
113+
114+
input_array = np.array(input_array, dtype=np.float32)
115+
output_array = np.array(
116+
[(0, 0), (0, height), (width, height), (width, 0)], dtype=np.float32
117+
)
118+
119+
matrix = cv2.getPerspectiveTransform(input_array, output_array)
120+
result = cv2.warpPerspective(
121+
image,
122+
matrix,
123+
(width, height),
124+
borderMode=cv2.BORDER_CONSTANT,
125+
borderValue=(0, 0, 0),
126+
)
127+
128+
return result
129+
130+
131+
if __name__ == "__main__":
132+
approx_polygon = pipeline().reshape((-1, 2))
133+
print(len(approx_polygon))
134+
draw_points(approx_polygon)
135+
perspective = do_perspective_transformation(
136+
cv2.imread(
137+
"./yolo/yolo_predictions/medicakolkata_rcu_mon--8_2022_5_18_17_5_38.jpeg"
138+
),
139+
approx_polygon,
140+
)
141+
cv2.imshow("perspective", perspective)
142+
cv2.waitKey(0)
143+
cv2.destroyAllWindows()

0 commit comments

Comments
 (0)