Thompson001 commited on
Commit
10d3add
·
verified ·
1 Parent(s): c115f24

Upload 2 files

Browse files
Files changed (2) hide show
  1. cv2_utils.py +174 -0
  2. inference_utils.py +132 -0
cv2_utils.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from scipy.spatial import distance as dist
3
+ from imutils import perspective
4
+ from imutils import contours
5
+ import numpy as np
6
+ import argparse
7
+ import imutils
8
+ import cv2
9
+ from functools import reduce
10
+ from scipy.interpolate import interp1d
11
+ import math
12
+
13
+ def midpoint(ptA, ptB):
14
+ return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
15
+
16
+ def extract_bboxes(fused):
17
+ """Compute bounding boxes from masks.
18
+ mask: [height, width]..
19
+ Returns: bbox array [num_instances, (y1, x1, y2, x2)].
20
+ """
21
+ mask = cv2.cvtColor(fused, cv2.COLOR_BGR2GRAY)
22
+ mask[mask < 40] = 0
23
+ mask[mask >= 40] = 1
24
+ mask = mask.reshape(256, 256, 1)
25
+ boxes = np.zeros([mask.shape[-1], 4], dtype=np.int32)
26
+ for i in range(mask.shape[-1]):
27
+ m = mask[:, :, i]
28
+ # Bounding box.
29
+ horizontal_indicies = np.where(np.any(m, axis=0))[0]
30
+ vertical_indicies = np.where(np.any(m, axis=1))[0]
31
+ if horizontal_indicies.shape[0]:
32
+ x1, x2 = horizontal_indicies[[0, -1]]
33
+ y1, y2 = vertical_indicies[[0, -1]]
34
+ # x2 and y2 should not be part of the box. Increment by 1.
35
+ x2 += 1
36
+ y2 += 1
37
+ else:
38
+ # No mask for this instance. Might happen due to
39
+ # resizing or cropping. Set bbox to zeros
40
+ x1, x2, y1, y2 = 0, 0, 0, 0
41
+ boxes[i] = np.array([y1, x1, y2, x2])
42
+ return boxes.astype(np.int32)
43
+
44
+ def getContours(npImage, overlay_img, realHeight, realWidth, unit, confidence, angle_th=30):
45
+ # load the image, convert it to grayscale, and blur it slightly
46
+ image = npImage.copy()#cv2.imread(imagePath)
47
+ # image size
48
+ imgHeight = image.shape[0]
49
+ imgWidth = image.shape[1]
50
+
51
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
52
+ gray = cv2.GaussianBlur(gray, (3, 3), 0)
53
+
54
+ # perform edge detection, then perform a dilation + erosion to
55
+ # close gaps in between object edges
56
+ edged = cv2.Canny(gray, 50, 80)
57
+
58
+ edged = cv2.dilate(edged, None, iterations=1)
59
+ edged = cv2.erode(edged, None, iterations=1)
60
+ # find contours in the edge map
61
+ cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
62
+ cv2.CHAIN_APPROX_SIMPLE)
63
+ #cv2.drawContours(image=overlay_img, contours=cnts[0], contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
64
+ cnts = imutils.grab_contours(cnts)
65
+
66
+ # sort the contours from left-to-right and initialize the
67
+ # 'pixels per metric' calibration variable
68
+ (cnts, _) = contours.sort_contours(cnts)
69
+ pixelsPerMetricHeight = realHeight/imgHeight
70
+ pixelsPerMetricWidth = realWidth/imgWidth
71
+ #draw bounding box
72
+ y1, x1, y2, x2 = extract_bboxes(npImage)[0]
73
+ cv2.rectangle(overlay_img,(x1,y1),(x2, y2),(0,255,0),2)
74
+ # loop over the contours individually
75
+
76
+ for c in cnts:
77
+ # if the contour is not sufficiently large, ignore it
78
+ if cv2.contourArea(c) < 100:
79
+ continue
80
+ # compute the rotated bounding box of the contour
81
+ orig = overlay_img.copy()
82
+ box = cv2.minAreaRect(c)
83
+ box = cv2.boxPoints(box)
84
+ box = np.array(box, dtype="int")
85
+ # order the points in the contour such that they appear
86
+ # in top-left, top-right, bottom-right, and bottom-left
87
+ # order, then draw the outline of the rotated bounding
88
+ # box
89
+ box = perspective.order_points(box)
90
+
91
+ # unpack the ordered bounding box, then compute the midpoint
92
+ # between the top-left and top-right coordinates, followed by
93
+ # the midpoint between bottom-left and bottom-right coordinates
94
+ (tl, tr, br, bl) = box
95
+ (tltrX, tltrY) = midpoint(tl, tr)
96
+ (blbrX, blbrY) = midpoint(bl, br)
97
+ # compute the midpoint between the top-left and top-right points,
98
+ # followed by the midpoint between the top-righ and bottom-right
99
+ (tlblX, tlblY) = midpoint(tl, bl)
100
+ (trbrX, trbrY) = midpoint(tr, br)
101
+
102
+ # draw lines between the midpoints
103
+ #cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),
104
+ #(255, 0, 0), 1)
105
+ cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),
106
+ (0, 0, 255), 1)
107
+
108
+
109
+ top_p = min([(int(tlblX), int(tlblY)), (int(trbrX), int(trbrY))], key=lambda x : x[1])
110
+ bot_p = max([(int(tlblX), int(tlblY)), (int(trbrX), int(trbrY))], key=lambda x : x[1])
111
+ D_ad = ((top_p[1] - bot_p[1]) ** 2 + (top_p[0] - bot_p[0])**2) ** 0.5 + 1e-7
112
+
113
+ P1 = min(top_p, bot_p, key=lambda x:x[0])
114
+ P2 = max(top_p, bot_p, key=lambda x:x[0])
115
+ slope = (P1[1] - P2[1]) / (P2[0] - P1[0]) if (P2[0] - P1[0]) != 0 else 0
116
+ cat = ''
117
+ angle = 0
118
+ if slope > 0:
119
+ angle = np.arccos((top_p[0] - bot_p[0])/D_ad) * 180 / math.pi
120
+ cv2.putText(orig, "angle={:.1f}".format(angle),
121
+ (max(top_p[0]-100, 0), top_p[1] + 15), cv2.FONT_HERSHEY_DUPLEX,
122
+ 0.45, (0, 0, 255), 1)
123
+ else:
124
+ angle = np.arccos((bot_p[0] - top_p[0])/D_ad) * 180 / math.pi
125
+ cv2.putText(orig, "angle={:.1f}".format(angle),
126
+ (top_p[0], top_p[1] + 15), cv2.FONT_HERSHEY_DUPLEX,
127
+ 0.45, (0, 0, 255), 1)
128
+
129
+
130
+ # compute the Euclidean distance between the midpoints
131
+ dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
132
+ dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))
133
+ length = cv2.arcLength(c, True) / 2. * pixelsPerMetricWidth
134
+ M = cv2.moments(c)
135
+ cX = int(M["m10"] / M["m00"])
136
+ cY = int(M["m01"] / M["m00"])
137
+
138
+ # width
139
+ mask = gray.copy()
140
+ mask[mask < 40] = 0
141
+ width = cv2.countNonZero(mask[cY][:])
142
+ right_most_x = np.max(np.nonzero(mask[cY][:]))
143
+ left_most_x = np.min(np.nonzero(mask[cY][:]))
144
+ cv2.line(orig, (int(left_most_x), int(cY)), (int(right_most_x), int(cY)),
145
+ (0, 0, 255), 1)
146
+ width *= pixelsPerMetricWidth
147
+ # compute the size of the object
148
+ dimA = dA * pixelsPerMetricHeight
149
+ dimB = dB * pixelsPerMetricWidth
150
+ if angle < angle_th:
151
+ cat +='H'
152
+ cv2.putText(orig, "L={:.1f}".format(length) + unit,
153
+ (int(tltrX), int(tltrY) + 40), cv2.FONT_HERSHEY_DUPLEX,
154
+ 0.45, (0, 0, 255), 1)
155
+ cv2.putText(orig, "W={:.1f}".format(width) + unit,
156
+ (int(tltrX), int(tltrY) + 55), cv2.FONT_HERSHEY_DUPLEX,
157
+ 0.45, (0, 0, 255), 1)
158
+ else:
159
+ cat += 'V'
160
+ cv2.putText(orig, "L={:.1f}".format(length) + unit,
161
+ (int(tltrX), int(tltrY)), cv2.FONT_HERSHEY_DUPLEX,
162
+ 0.45, (0, 0, 255), 1)
163
+ cv2.putText(orig, "W={:.1f}".format(width) + unit,
164
+ (int(tltrX), int(tltrY) + 15), cv2.FONT_HERSHEY_DUPLEX,
165
+ 0.45, (0, 0, 255), 1)
166
+ if slope > 0:
167
+ cat += 'L'
168
+ else:
169
+ cat += 'R'
170
+ cv2.putText(orig, "Crack {:.2f}%".format(confidence.item()*100) + " cat="+ cat, (x1, max(0, y1-5)), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (36,255,12), 1)
171
+
172
+ return orig
173
+
174
+
inference_utils.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ import cv2
4
+ import torch
5
+ import numpy as np
6
+ from PIL import Image
7
+ from io import BytesIO
8
+ from .cv2_utils import getContours
9
+ import torchvision.transforms as transforms
10
+ from .models.deepcrack_model import DeepCrackModel
11
+
12
+ def tensor2im(input_image, imtype=np.uint8):
13
+ """"Converts a Tensor array into a numpy image array.
14
+
15
+ Parameters:
16
+ input_image (tensor) -- the input image tensor array
17
+ imtype (type) -- the desired type of the converted numpy array
18
+ """
19
+ if not isinstance(input_image, np.ndarray):
20
+ if isinstance(input_image, torch.Tensor): # get the data from a variable
21
+ image_tensor = input_image.data
22
+ else:
23
+ return input_image
24
+ image_numpy = image_tensor[0].cpu().float().numpy() # convert it into a numpy array
25
+ if image_numpy.shape[0] == 1: # grayscale to RGB
26
+ image_numpy = np.tile(image_numpy, (3, 1, 1))
27
+ image_numpy = (np.transpose(image_numpy, (1, 2, 0)) + 1) / 2.0 * 255.0 # post-processing: tranpose and scaling
28
+ else: # if it is a numpy array, do nothing
29
+ image_numpy = input_image
30
+ return image_numpy.astype(imtype)
31
+
32
+ def bytes_to_array(b: bytes) -> np.ndarray:
33
+ np_bytes = BytesIO(b)
34
+ return np.load(np_bytes, allow_pickle=True)
35
+
36
+ def read_image(bytesImg, dim=(256, 256)): #Decode Bytes to array
37
+ img_transforms = transforms.Compose([transforms.ToTensor(),
38
+ transforms.Normalize((0.5, 0.5, 0.5),
39
+ (0.5, 0.5, 0.5))])
40
+ img = np.fromstring(bytesImg, np.uint8)
41
+ img = cv2.imdecode(img, cv2.IMREAD_COLOR)
42
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
43
+
44
+ # adjust the image size
45
+ w, h = dim
46
+ if w > 0 or h > 0:
47
+ img = cv2.resize(img, (w, h), interpolation=cv2.INTER_CUBIC)
48
+
49
+ # apply the transform to both A and B
50
+ img = img_transforms(Image.fromarray(img.copy()))
51
+ return img
52
+
53
+ def create_model(opt, cp_path='pretrained_net_G.pth'):
54
+ model = DeepCrackModel(opt) # create a model given opt.model and other options
55
+ checkpoint = torch.load(cp_path)
56
+ if hasattr(model.netG, 'module'):
57
+ model.netG.module.load_state_dict(checkpoint, strict=False)
58
+ else:
59
+ model.netG.load_state_dict(checkpoint, strict=False)
60
+ model.eval()
61
+ return model
62
+
63
+ def overlay(
64
+ image: np.ndarray,
65
+ mask: np.ndarray,
66
+ color = (255, 0, 0),
67
+ alpha: float = 0.5,
68
+ resize = (256, 256)
69
+ ) -> np.ndarray:
70
+ """Combines image and its segmentation mask into a single image.
71
+
72
+ Params:
73
+ image: Training image.
74
+ mask: Segmentation mask.
75
+ color: Color for segmentation mask rendering.
76
+ alpha: Segmentation mask's transparency.
77
+ resize: If provided, both image and its mask are resized before blending them together.
78
+
79
+ Returns:
80
+ image_combined: The combined image.
81
+
82
+ """
83
+ color = np.asarray(color).reshape(1, 1, 3)
84
+ colored_mask = np.expand_dims(mask, 0).repeat(3, axis=2)
85
+ masked = np.ma.MaskedArray(image, mask=colored_mask, fill_value=color)
86
+ image_overlay = masked.filled()
87
+
88
+ if resize is not None:
89
+ image = cv2.resize(image, resize)
90
+ image_overlay = cv2.resize(image_overlay, resize)
91
+
92
+ image_combined = cv2.addWeighted(image, 1 - alpha, image_overlay, alpha, 0)
93
+
94
+ return image_combined
95
+
96
+ def inference(model, bytesImg, dim, unit):
97
+ #print(img_path)
98
+
99
+ image = read_image(bytesImg) #Read Array
100
+ # batchify
101
+ image = image.unsqueeze(0)
102
+ # hacky way to pass ground truth label
103
+ model.set_input({'image': image, 'label': torch.zeros_like(image), 'A_paths':''})
104
+ model.test() # run inference
105
+ visuals = model.get_current_visuals() # get image results
106
+ confidence = visuals['fused'].max()
107
+
108
+ # fused for final prediction
109
+ for key in visuals.keys():
110
+ visuals[key] = tensor2im(visuals[key])
111
+
112
+ h, w, _ = visuals['fused'].shape
113
+ fused = Image.fromarray(visuals['fused'])
114
+ fused = np.array(fused, dtype='uint8')
115
+ realHeight=dim[1]
116
+ realWidth=dim[0]
117
+
118
+ mask = cv2.cvtColor(fused, cv2.COLOR_BGR2GRAY)
119
+ mask[mask < 90] = 0
120
+ mask[mask >= 90] = 255
121
+ cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL,
122
+ cv2.CHAIN_APPROX_SIMPLE)
123
+
124
+
125
+ overlay_img = overlay(tensor2im(image), mask, alpha=0)
126
+ cv2.drawContours(image=overlay_img, contours=cnts[0], contourIdx=-1, color=(0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
127
+ contour_img = getContours(fused, overlay_img, realHeight, realWidth, unit, confidence)
128
+
129
+ return contour_img if contour_img is not None else overlay_img, visuals
130
+
131
+
132
+