Afin de faire votre propre détection d'objets avec OpenCV, il est nécessaire de découper une grande quantité d'images comme données d'apprentissage. J'ai donc créé deux outils.
Découpez l'image avec une interface graphique simple et générez un fichier à transmettre à opencv_createsamples.exe. Le code est ci-dessous. Comme il a été fait par un travail urgent, il peut y avoir de nombreux bugs.
La structure des répertoires est la suivante.
Annuaire approprié/
├── images/
|├── Fichier image 1.png
|├── Fichier image 2.jpg
| | ︙
|└── Fichier image n.bmp
├── clipper.py
├── make_negative.py
├── opencv_createsamples.exe
├── opencv_traincascade.exe
└── (DLL OpenCV)
--Cliquez à gauche et faites glisser pour sélectionner la plage d'objets que vous souhaitez détecter --Cliquez avec le bouton droit pour supprimer la dernière plage sélectionnée
Une fois terminé, un fichier pos.dat
qui peut être entré dans opencv_createsamples.exe sera généré.
La progression est enregistrée dans un fichier, vous pouvez donc reprendre le travail lorsque vous avez terminé.
Utilisez simplement l'outil de support de recadrage d'image, puis exécutez make_negative.py
.
Cela générera une image d'exemple négative dans le répertoire negatives
et une liste d'exemples négatifs dans bg.dat
.
Pour le moment, exécutez simplement la commande ci-dessous.
opencv_createsamples.exe -info pos.dat -vec pos.vec
opencv_traincascade.exe -répertoire de sortie de données-vec pos.vec -bg bg.dat
clipper.py
import cv2
import glob
import lzma
import os
import pickle
file_dir = './images'
state_file = './data'
output_file = './pos.dat'
display_size = 768
window_name = 'image_clip'
state = {}
mouse_position = [0, 0]
mouse_wheel = 0
crop_origin = None
crop_end = None
selecting = False
remove = False
def load_state():
global state
if os.path.exists(state_file):
with lzma.open(state_file, 'rb') as f:
state = pickle.load(f)
def save_state():
with lzma.open(state_file, 'wb') as f:
pickle.dump(state, f)
def output():
with open(output_file, 'w') as f:
for file_name, rects in state.items():
if len(rects) == 0:
continue
values = [os.path.abspath(file_name), str(len(rects))]
values += sum([[str(int(r)) for r in rect] for rect in rects], [])
f.write(' '.join(values) + '\n')
def mouse_callback(event, x, y, flags, param):
global mouse_position
global mouse_wheel
global crop_origin
global selecting
global crop_end
global remove
mouse_position = (x, y)
if event == cv2.EVENT_LBUTTONDOWN:
crop_origin = mouse_position
selecting = True
if event == cv2.EVENT_LBUTTONUP:
crop_end = mouse_position
selecting = False
if event == cv2.EVENT_RBUTTONDOWN:
remove = True
if event == cv2.EVENT_MOUSEWHEEL:
mouse_wheel = flags
def main():
global state
global mouse_wheel
global crop_origin
global crop_end
global remove
os.makedirs(file_dir, exist_ok=True)
image_files = glob.glob(os.path.join(file_dir, '*'))
if len(image_files) == 0:
print('Veuillez mettre l'image en images')
exit()
load_state()
cv2.namedWindow(window_name, cv2.WINDOW_AUTOSIZE)
cv2.setMouseCallback(window_name, mouse_callback)
image_counter = 0
for i in range(len(image_files)):
image_counter = i
if image_files[image_counter] not in state.keys():
break
while True:
if image_counter < 0:
image_counter = image_counter + len(image_files)
if image_counter >= len(image_files):
image_counter = image_counter - len(image_files)
save_state()
image_file = image_files[image_counter]
image = cv2.imread(image_file)
scale = display_size / max(image.shape[0], image.shape[1])
resized_image = cv2.resize(image,
dsize=None,
fx=scale,
fy=scale,
interpolation=cv2.INTER_AREA)
if image_file not in state:
state[image_file] = []
while True:
display_image = resized_image.copy()
for rect in state[image_file]:
left_top = (int(rect[0] * scale), int(rect[1] * scale))
right_bottom = (int((rect[0] + rect[2]) * scale), int((rect[1] + rect[3]) * scale))
display_image = cv2.rectangle(display_image,
left_top,
right_bottom,
(0, 0, 255),
2)
display_image = cv2.line(display_image,
(mouse_position[0], 0),
(mouse_position[0], display_image.shape[0]),
(255, 0, 0),
2)
display_image = cv2.line(display_image,
(0, mouse_position[1]),
(display_image.shape[1], mouse_position[1]),
(255, 0, 0),
2)
if selecting:
display_image = cv2.rectangle(display_image,
crop_origin,
mouse_position,
(0, 128, 255),
2)
cv2.imshow(window_name, display_image)
key = cv2.waitKey(10) & 0xFF
if crop_origin is not None and crop_end is not None:
rect_x = min(mouse_position[0], crop_origin[0])
rect_w = max(mouse_position[0], crop_origin[0]) - rect_x
rect_y = min(mouse_position[1], crop_origin[1])
rect_h = max(mouse_position[1], crop_origin[1]) - rect_y
new_rect = [rect_x / scale, rect_y / scale, rect_w / scale, rect_h / scale]
state[image_file].append(new_rect)
crop_origin = None
crop_end = None
if remove:
if len(state[image_file]) > 0:
state[image_file].pop(-1)
remove = False
if mouse_wheel != 0:
image_counter += 1 if mouse_wheel > 0 else -1
mouse_wheel = 0
break
if key == ord('q') or key == 27:
return
if __name__ == '__main__':
main()
cv2.destroyAllWindows()
save_state()
output()
make_negative.py
import cv2
import glob
import lzma
import os
import pickle
import random
file_dir = './images'
output_dir = './negatives/'
output_list_file = './bg.dat'
state_file = './data'
def sample_start_point(width, height, positive_rects):
for i in range(100):
start_point = [random.randrange(width), random.randrange(height)]
for rect in positive_rects:
rect_left = rect[0]
rect_right = rect[0] + rect[2]
rect_top = rect[1]
rect_bottom = rect[1] + rect[3]
if ((rect_left <= start_point[0] and rect_right >= start_point[0]) and
(rect_top <= start_point[1] and rect_bottom >= start_point[1])):
break
else:
return start_point
return None
if __name__ == '__main__':
if not os.path.exists(state_file):
exit()
with lzma.open(state_file, 'rb') as f:
state = pickle.load(f)
image_counter = 0
for file_name, positive_rects in state.items():
if len(positive_rects) == 0:
continue
image = cv2.imread(file_name)
width = image.shape[1]
height = image.shape[0]
negative_rects = []
for i in range(1000):
start_point = sample_start_point(width, height, positive_rects)
if start_point is None:
continue
negative_rect = [start_point[0], start_point[1], start_point[0], start_point[1]]
min_x = 0
max_x = width
min_y = 0
max_y = height
directions = random.sample(['left', 'right', 'up', 'down'], 4)
for direction in directions:
for positive_rect in positive_rects:
positive_rect_left = positive_rect[0]
positive_rect_right = positive_rect[0] + positive_rect[2]
positive_rect_top = positive_rect[1]
positive_rect_bottom = positive_rect[1] + positive_rect[3]
if not (negative_rect[1] > positive_rect_bottom or
negative_rect[3] < positive_rect_top):
if direction == 'left':
if negative_rect[0] > positive_rect_right:
min_x = max(min_x, positive_rect_right)
if direction == 'right':
if negative_rect[2] < positive_rect_left:
max_x = min(max_x, positive_rect_left)
if not (negative_rect[0] > positive_rect_right or
negative_rect[2] < positive_rect_left):
if direction == 'up':
if negative_rect[1] > positive_rect_bottom:
min_y = max(min_y, positive_rect_bottom)
if direction == 'down':
if negative_rect[3] < positive_rect_top:
max_y = min(max_y, positive_rect_top)
if direction == 'left':
negative_rect[0] = min_x
if direction == 'right':
negative_rect[2] = max_x
if direction == 'up':
negative_rect[1] = min_y
if direction == 'down':
negative_rect[3] = max_y
if negative_rect[0] == negative_rect[2] or negative_rect[1] == negative_rect[3]:
continue
negative_rects.append(tuple([int(x) for x in negative_rect]))
negative_rects = set(negative_rects)
for negative_rect in negative_rects:
trimed_image = image[negative_rect[1]:negative_rect[3], negative_rect[0]:negative_rect[2], :]
os.makedirs(output_dir, exist_ok=True)
extention = os.path.splitext(file_name)[1]
output_file_path = os.path.join(output_dir, '{}{}'.format(image_counter, extention))
cv2.imwrite(output_file_path, trimed_image)
image_counter += 1
image_files = glob.glob(os.path.join(output_dir, '*'))
with open(output_list_file, 'w') as f:
for image_file in image_files:
f.write('{}\n'.format(os.path.abspath(image_file)))
Recommended Posts