Um mit OpenCV Ihre eigene Objekterkennung zu erstellen, müssen Sie eine große Anzahl von Bildern als Trainingsdaten ausschneiden. Also habe ich zwei Tools erstellt.
Schneiden Sie das Bild mit einer einfachen Benutzeroberfläche aus und generieren Sie eine Datei, die an opencv_createsamples.exe übergeben werden soll. Der Code ist unten. Da es durch Eilarbeit gemacht wurde, kann es viele Fehler geben.
Die Verzeichnisstruktur ist wie folgt.
Passendes Verzeichnis/
├── images/
|├── Bilddatei 1.png
|├── Bilddatei 2.jpg
| | ︙
|└── Bilddatei n.bmp
├── clipper.py
├── make_negative.py
├── opencv_createsamples.exe
├── opencv_traincascade.exe
└── (OpenCV-DLLs)
Wenn Sie fertig sind, wird eine Datei pos.dat
generiert, die in opencv_createsamples.exe eingegeben werden kann.
Der Fortschritt wird in einer Datei gespeichert, sodass Sie die Arbeit fortsetzen können, wenn Sie fertig sind.
Verwenden Sie einfach das Support-Tool zum Zuschneiden von Bildern und führen Sie dann "make_negative.py" aus. Dies erzeugt ein negatives Beispielbild im Verzeichnis "Negative" und eine Liste negativer Beispiele in "bg.dat".
Führen Sie vorerst einfach den folgenden Befehl aus.
opencv_createsamples.exe -info pos.dat -vec pos.vec
opencv_traincascade.exe -Datenausgabeverzeichnis-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('Bitte setzen Sie das Bild in Bilder')
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