Requirement:
The problem is to architect and train a model which is able to output the parameters of the circle present inside of a given image under the presence of noise. The model should output a circle parameterized by (row, column, radius) which specifies the center coordinates of the circle in the image and the radius of the circle. Please do not use a pre-trained architecture/model. (Excluding model training time)
Example Images:
See attached
Deliverables: (All 3 required)
Trained model and working find_circle method
The standard output of the model training in a file called training output.txt to make sure that the training loss is visible in the output logs.
The code used to define & train the model
Evaluation Criteria:
Try to get as high of a score as you can but .9 is the minimum for your submission to consider using the metric AP@0.7. The provided main function will evaluate this metric in 1000 examples with a noise level of 2, we will use this main function to evaluate the model you produce.
Model Architecture. You do not need to use an unnecessarily large CNN backbone (> 10 convolutional/linear layers or >1M trainable parameters)
Loss function and optimizer used to train the model
Code quality and cleanliness. Please make sure to follow the general python conventions
The attachments are only samples. You should generate your own data w/the function provided. Use main.py to generate the image and write a find_circle() function inside the main.py. You can use OpenCV or TensorFlow or PyTorch, or any other things that you need.
Main.py
import Libraries
import numpy as np
from shapely.geometry.point import Point
from skimage.draw import circle_perimeter_aa
import matplotlib.pyplot as plt
from tensorflow.keras.models import load_model
def draw_circle(img, row, col, rad):
rr, cc, val = circle_perimeter_aa(row, col, rad)
valid = (
(rr >= 0) &
(rr < img.shape[0]) &
(cc >= 0) &
(cc < img.shape[1])
)
img[rr[valid], cc[valid]] = val[valid]
def noisy_circle(size, radius, noise):
img = np.zeros((size, size), dtype=np.float)
# Circle
row = np.random.randint(size)
col = np.random.randint(size)
rad = np.random.randint(10, max(10, radius))
draw_circle(img, row, col, rad)
# Noise
img += noise * np.random.rand(*img.shape)
return (row, col, rad), img
def find_circle(img):
# load model
model = load_model('circle_200.md')
# reshape input
data = img.reshape(1, -1)
# predict
prediction = model.predict(data)
prediction = prediction.reshape(3, 1)
return prediction
def iou(params0, params1):
row0, col0, rad0 = params0
row1, col1, rad1 = params1
shape0 = Point(row0, col0).buffer(rad0)
shape1 = Point(row1, col1).buffer(rad1)
return (
shape0.intersection(shape1).area /
shape0.union(shape1).area
)
def main():
results = []
for _ in range(1000):
print(_)
params, img = noisy_circle(200, 50, 2)
detected = find_circle(img)
results.append(iou(params, detected))
results = np.array(results)
print((results > 0.7).mean())
main()
import numpy as np
from skimage.draw import circle_perimeter_aa
def draw_circle(img, row, col, rad):
rr, cc, val = circle_perimeter_aa(row, col, rad)
valid = (
(rr >= 0) &
(rr < img.shape[0]) &
(cc >= 0) &
(cc < img.shape[1])
)
img[rr[valid], cc[valid]] = val[valid]
def noisy_circle(size, radius, noise):
img = np.zeros((size, size), dtype=np.float)
# Circle
row = np.random.randint(size)
col = np.random.randint(size)
rad = np.random.randint(10, max(10, radius))
draw_circle(img, row, col, rad)
# Noise
img += noise * np.random.rand(*img.shape)
return (row, col, rad), img
build_model.py
#import Libraries
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from circle import noisy_circle
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Dropout
from tensorflow.keras.layers import Convolution2D, MaxPooling2D, Flatten
size = 200 # image size
bunch = 20000 # number of images
max_rad = 50 #size // 2 # maximum radius of circle
# containers for images and parameters
images = np.zeros((bunch, size, size), dtype=np.float)
circles = np.zeros((bunch, 3))
# generate bunch of images
for i in range(bunch):
circles[i], images[i] = noisy_circle(size, max_rad, 2)
# reshape and normalize the image data to mean 0 and std 1
X = (images.reshape(bunch, -1))# - np.mean(images)) / np.std(images)
print(f"\nReshaped image data: {X.shape}, {np.mean(X)}, {np.std(X)}")
# normalize circles' parameters to values between 0 and 1
y = circles.reshape(bunch, -1)# / size
print(f"Reshaped circles' parameters: {y.shape}, {np.mean(y)}, {np.std(y)}\n")
# split training and test sets
k = int(0.8 * bunch) # size of train set
# train
train_X = X[:k]
train_y = y[:k]
# test
test_X = X[k:]
test_y = y[k:]
# build model
model = Sequential()
model.add(Dense(128, input_dim=X.shape[-1], activation='relu'))
model.add(Dropout(0.2))
#model.add(Dense(256, activation='relu'))
#model.add(Dropout(0.3))
model.add(Dense(y.shape[-1]))
model.compile('adadelta', 'mse', metrics=['accuracy'])
# train model
model.fit(train_X, train_y, epochs=20, validation_data=(test_X, test_y), verbose=2)
# save model
model.save('circle.md')
# predict circle parameters on test images
pred_para = model.predict(test_X)
#pred_para = pred_y * size
pred_para = pred_para.reshape(len(pred_para), -1)
# plot an example
test_imgs = images[k:]
test_para = circles[k:]
k = 3
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Original")
plt.imshow(test_imgs[k])
plt.gca().add_patch(matplotlib.patches.Circle((test_para[k][1], test_para[k][0]), test_para[k][2], ec='r', fill=False))
plt.subplot(1, 2, 2)
plt.title("Prediction")
plt.imshow(test_imgs[k])
plt.gca().add_patch(matplotlib.patches.Circle((pred_para[k][1], pred_para[k][0]), pred_para[k][2], ec='r', fill=False))
plt.show()
Output
Comments