diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..615aafb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/usr/bin/python3" +} \ No newline at end of file diff --git a/README.md b/README.md index 01fbe19..6c4caee 100644 --- a/README.md +++ b/README.md @@ -606,3 +606,179 @@ while True: ``` В данном примере робот ездиет по черной линии, также светодтодами подсвечивается сенсор под которым обнаружина линия. delta_sensor это пороговое значения срабатывания сенсора, оно может менятся в зависимости от освещения. Это значение можно узнать если воспользоватся примером выше который считывает значения с сенсоров. + +## Компьютерное зрение + +В рамках данного блока я предлогаю рассмотреть пример который позволит распознать зеленый круг, а также взаимодействавать с ним. Этот процесс состоит из 2х этапов, первое это подбор параметров, а второе это пепосредственная работа с программой по распознованию. Для риализации нужно сделать следующее: + +1. нужно доустановить необходимые пакеты, для того что-бы мы могли подключится к пишке, и видить экран, чтобы иметь возможность видеть рабочий стол. + +```bash +sudo apt install realvnc-vnc-server realvnc-vnc-viewer +sudo raspi-config +# Interfacing Options -> VNC -> YES +vncserver +``` + +Потом переходим сюда (https://www.realvnc.com/en/connect/download/viewer/) и качаем оттуда версию для винды. + +2. Поставим пакеты для работы с компьютерным зрением: + +```bash +sudo apt install libatlas-base-dev +pip3 install numpy opencv-python +``` + +3. Далее запускаем скрипт предназначенный для подбора параметров. + +```python +find_filter.py +``` + +![cv1](pic/CV/cv_1.png) + +Наша задача подобрать минимальное и максимальное значения для 3х парматров h, s, v. Сначало нужно подобрать нижнюю границу параметра h (h1) Для этого нужно двигать первый ползунок до тех пор пока круг не станет быть виден. потом подгоняем максимальное значение h (h2) так чтобы круз все еще отчетливо был виден. Потом также подгоняем второй параметр s и третий v. В итоге должно получится что-то похожее на это: + +![cv2](pic/CV/cv_2.png) + +Далее нужно сожранить последнее значение распечатанное в консоле, там будет что-то похожее на это: + +```txt +(96, 175,70),(85,255,117) +``` + +Эти числа нам потребуются далее для работы с распознованием обьекта. + +Далее запустим пример который распознает круг. + +```python +circul_detect.py +``` + +![cv3](pic/CV/cv_3.png) + +TODO: Написть че вообще происходит. + + +## Машинное обучение + +Тут в качкстве примера я предложу вам обучить свою нейронную серь на открытом датасете который содержит тысячи изображений цифр - MNIST. Этот датасет уже встроен в библиотеку keras. + +Для того чтобы использовать нейронную сеть ее нужно сначала обучить, для этого я предлагаю использовать сервис google colaboratiry - https://colab.research.google.com/notebooks/intro.ipynb, потомучто обучение сетей требует значительных вычислительных мощностей, а гугл готов нам предоставить их бесплатно в образовательных целях. Данный сервис представляет из себя страницу jupiter noutbook код в которой исполняется поблочно. давайте рассмотрим блоки которые нам необходимы для получения обученной сети: + +1) Импорт библиотек + +```python +import numpy as np +from tensorflow import keras +from tensorflow.keras import layers +``` + +2) Скачивание датасета и преобразованиего в необходимый формат: + +```python +# Model / data parameters +num_classes = 10 +input_shape = (28, 28, 1) + +# the data, split between train and test sets +(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() + +# Scale images to the [0, 1] range +x_train = x_train.astype("float32") / 255 +x_test = x_test.astype("float32") / 255 +# Make sure images have shape (28, 28, 1) +x_train = np.expand_dims(x_train, -1) +x_test = np.expand_dims(x_test, -1) +print("x_train shape:", x_train.shape) +print(x_train.shape[0], "train samples") +print(x_test.shape[0], "test samples") + + +# convert class vectors to binary class matrices +y_train = keras.utils.to_categorical(y_train, num_classes) +y_test = keras.utils.to_categorical(y_test, num_classes) +``` + +3) Создание нейронной сети: +TODO: написать что-то про то откуда взялись эти слои + +```python +model = keras.Sequential( + [ + keras.Input(shape=input_shape), + layers.Conv2D(32, kernel_size=(3, 3), activation="relu"), + layers.MaxPooling2D(pool_size=(2, 2)), + layers.Conv2D(64, kernel_size=(3, 3), activation="relu"), + layers.MaxPooling2D(pool_size=(2, 2)), + layers.Flatten(), + layers.Dropout(0.5), + layers.Dense(num_classes, activation="softmax"), + ] +) + +model.summary() +``` + +В резулитате можно увидить структуру созданой сети: + +```txt +Model: "sequential" +_________________________________________________________________ +Layer (type) Output Shape Param # +================================================================= +conv2d (Conv2D) (None, 26, 26, 32) 320 +_________________________________________________________________ +max_pooling2d (MaxPooling2D) (None, 13, 13, 32) 0 +_________________________________________________________________ +conv2d_1 (Conv2D) (None, 11, 11, 64) 18496 +_________________________________________________________________ +max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64) 0 +_________________________________________________________________ +flatten (Flatten) (None, 1600) 0 +_________________________________________________________________ +dropout (Dropout) (None, 1600) 0 +_________________________________________________________________ +dense (Dense) (None, 10) 16010 +================================================================= +Total params: 34,826 +Trainable params: 34,826 +Non-trainable params: 0 +_________________________________________________________________ +``` + +4) Обучение сетки. Это долгий процесс, у меня заняло около 30 мин. + +```python +batch_size = 128 +epochs = 15 + +model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"]) + +model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1) +``` + +5) Проверка точности и сохранение обученой сети. + +```python +score = model.evaluate(x_test, y_test, verbose=0) +model.save('mnist_trained_model.h5') +print("Test loss:", score[0]) +print("Test accuracy:", score[1]) +``` + +После этого нужно будет перести файл ```mnist_trained_model.h5``` на вышу малину. + +Даллее для того чтобы запустить это на пишки нужно доставить еще пакеты: + +```bash +pip3 install keras tensorflow +``` + +После этого запускаем + +```bash +number_detect.py +``` + +TODO: написать про то как это работает. \ No newline at end of file diff --git a/pic/CV/cv_1.png b/pic/CV/cv_1.png new file mode 100644 index 0000000..a77a18f Binary files /dev/null and b/pic/CV/cv_1.png differ diff --git a/pic/CV/cv_2.png b/pic/CV/cv_2.png new file mode 100644 index 0000000..4431c57 Binary files /dev/null and b/pic/CV/cv_2.png differ diff --git a/pic/CV/cv_3.png b/pic/CV/cv_3.png new file mode 100644 index 0000000..1b4eb5f Binary files /dev/null and b/pic/CV/cv_3.png differ diff --git a/python/circul_detect.py b/python/circul_detect.py new file mode 100644 index 0000000..a893f35 --- /dev/null +++ b/python/circul_detect.py @@ -0,0 +1,46 @@ +import cv2 +import numpy as np + +# IT IS WORK !!!!!! + +cam = cv2.VideoCapture(0) + +while True: + _, frame = cam.read() + hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) + hsv = cv2.blur(hsv,(5,5)) + + mask = cv2.inRange(hsv, (78,154,93),(86,224,255)) + + (contours, hierarchy) = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + cv2.drawContours(frame, contours, -1, (255,0,0), 3, cv2.LINE_AA, hierarchy, 1 ) + + max_radius = 0 + center = (0,0) + + for contour in contours: + (x,y),radius = cv2.minEnclosingCircle(contour) + if max_radius < int(radius): + max_radius = int(radius) + center = (int(x),int(y)) + + frame = cv2.circle(frame,center,max_radius,(0,255,0),2) + + S = 3.1415 * max_radius * max_radius + cv2.putText(frame, str(S), (30, 30),cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 10, 10), 2) + + if S > 100: + if S > 10000: + cv2.putText(frame, "UP", (30, 60),cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 10, 10), 2) + elif S < 5000: + cv2.putText(frame, "DOWN", (30, 60),cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 10, 10), 2) + + #cv2.imshow("Image with opening", mask) + #cv2.imshow("Image with closing", hsv) + + cv2.imshow("Image", frame) + + k = cv2.waitKey(2) + if k == 27: + cv2.destroyAllWindows() + break \ No newline at end of file diff --git a/python/find_filter.py b/python/find_filter.py new file mode 100644 index 0000000..ccce1f2 --- /dev/null +++ b/python/find_filter.py @@ -0,0 +1,66 @@ +import cv2 +import numpy as np + +if __name__ == '__main__': + def nothing(*arg): + pass + +h1_old = 0 +s1_old = 0 +v1_old = 0 +h2_old = 0 +s2_old = 0 +v2_old = 0 + +cv2.namedWindow( "result" ) # создаем главное окно +cv2.namedWindow( "settings" ) # создаем окно настроек + +cam = cv2.VideoCapture(0) +# создаем 6 бегунков для настройки начального и конечного цвета фильтра +cv2.createTrackbar('h1', 'settings', 0, 255, nothing) +cv2.createTrackbar('s1', 'settings', 0, 255, nothing) +cv2.createTrackbar('v1', 'settings', 0, 255, nothing) +cv2.createTrackbar('h2', 'settings', 255, 255, nothing) +cv2.createTrackbar('s2', 'settings', 255, 255, nothing) +cv2.createTrackbar('v2', 'settings', 255, 255, nothing) +crange = [0,0,0, 0,0,0] + +while True: + flag, img = cam.read() + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV ) + + # считываем значения бегунков + h1 = cv2.getTrackbarPos('h1', 'settings') + s1 = cv2.getTrackbarPos('s1', 'settings') + v1 = cv2.getTrackbarPos('v1', 'settings') + h2 = cv2.getTrackbarPos('h2', 'settings') + s2 = cv2.getTrackbarPos('s2', 'settings') + v2 = cv2.getTrackbarPos('v2', 'settings') + + # формируем начальный и конечный цвет фильтра + h_min = np.array((h1, s1, v1), np.uint8) + h_max = np.array((h2, s2, v2), np.uint8) + + # накладываем фильтр на кадр в модели HSV + thresh = cv2.inRange(hsv, h_min, h_max) + + cv2.imshow('result', thresh) + + ch = cv2.waitKey(5) + + if h1 != h1_old or s1 != s1_old or v1 != v1_old \ + or h2 != h2_old or s2 != s2_old or v2 != v2_old : + print ("resault: (%d,%d,%d),(%d,%d,%d)"%(h1,s1,v1,h2,s2,v2)) + + h1_old = h1 + s1_old = s1 + v1_old = v1 + h2_old = h2 + s2_old = s2 + v2_old = v2 + + if ch == 27: + break + +cam.release() +cv2.destroyAllWindows() \ No newline at end of file diff --git a/python/mnist_trained_model.h5 b/python/mnist_trained_model.h5 new file mode 100644 index 0000000..86992b9 Binary files /dev/null and b/python/mnist_trained_model.h5 differ diff --git a/python/number_detect.py b/python/number_detect.py new file mode 100644 index 0000000..dee6bb9 --- /dev/null +++ b/python/number_detect.py @@ -0,0 +1,98 @@ +import cv2 +import numpy as np +import keras + +''' +задачи +1) находить контур цифры +2) получать его координаты, вывести отдельным изображением +3) масштабировать его под нужный размер +4) загнать это на нейронную сеть https://habr.com/ru/post/466565/ +5) вывести результат + +очень сильно шумит изображение +''' + + +def find_number(frame): + # функция выполняет предобработку изображения + # находит область с цифрой по контурам + # далее в цикле перебераем найденные контуры + # если контур найден, то меняем разрешение на 28*28 + # выполняем бинаризацию, делим на 255 и инвертируем вычитая 1 + # flag нужен, чтобы он не выводил единицу, если цифра не найдена + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + blur = cv2.GaussianBlur(gray,(3,3), 0) + edges = cv2.Canny(blur, 50, 100) + contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + img2 = np.zeros((28, 28), np.uint8) + img_rec = np.zeros((1, 28, 28, 1), np.uint8) + fail = True + + for contr in contours: + + if cv2.contourArea(contr) < 500: + continue + x,y,w,h = cv2.boundingRect(contr) + img2 = frame[y-5:y+h+5, x-5:x+w+5] + + # Проверка на некорректное изображение + if img2.shape[0] <= 0 or img2.shape[1]<= 0: + continue + + #Фильтер по вертикальности рамки + if img2.shape[0] < img2.shape[1]: + continue + + img2 = cv2.resize(img2, (28, 28)) + img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) + + # th3 = cv2.adaptiveThreshold(img2,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\ + # cv2.THRESH_BINARY,11,2) + + _, th3 = cv2.threshold(img2,127,255,cv2.THRESH_BINARY) + + # Фильтруем по следнему цвету, должно быть много белого + avg_color_per_row = np.average(th3, axis=0) + avg_color = np.average(avg_color_per_row, axis=0) + if avg_color < 180: + continue + + # Если все норм то рисуем рамки на исходном изображении. + img = cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2) + + cv2.imshow("img2", img2) + cv2.imshow("th3", th3) + + img_rec = th3/255.0 + img_rec = 1 - img_rec + img_rec = img_rec.reshape((1,28,28,1)) + + fail = False + return img2, img_rec, fail + + + +if __name__ == '__main__': + cam = cv2.VideoCapture(0) + model = keras.models.load_model('mnist_trained_model.h5') + try: + while True: + _, frame = cam.read() + img_show, img_rec, fail = find_number(frame) + if fail == False: + result = model.predict_classes([img_rec]) + cv2.putText(frame,str(result[0]),(10,460), cv2.FONT_HERSHEY_SIMPLEX, 1,(0,0,255),2,cv2.LINE_AA) + + + cv2.imshow("Image", frame) + + k = cv2.waitKey(2) + if k == 27: + #cv2.imwrite('diff_robot_nn/nine.jpg', img_show) + cv2.destroyAllWindows() + break + except Exception as e: + cam.release() + cv2.destroyAllWindows() + print(e) \ No newline at end of file diff --git a/python/test_contur.py b/python/test_contur.py new file mode 100644 index 0000000..2a6752b --- /dev/null +++ b/python/test_contur.py @@ -0,0 +1,46 @@ +import cv2 +import numpy as np + +# NOT WORCK !!!! + +def find_contours_of_cards(image): + blurred = cv2.GaussianBlur(image, (3, 3), 0) + T, thresh_img = cv2.threshold(blurred, 215, 255, + cv2.THRESH_BINARY) + (cnts, _) = cv2.findContours(thresh_img, + cv2.RETR_EXTERNAL, + cv2.CHAIN_APPROX_SIMPLE) + return cnts + +def find_coordinates_of_cards(cnts, image): + cards_coordinates = {} + for i in range(0, len(cnts)): + x, y, w, h = cv2.boundingRect(cnts[i]) + if w > 20 and h > 30: + img_crop = image[y - 15:y + h + 15, + x - 15:x + w + 15] + cards_name = find_features(img_crop) + cards_coordinates[cards_name] = (x - 15, + y - 15, x + w + 15, y + h + 15) + return cards_coordinates + + + +cam = cv2.VideoCapture(0) + +while True: + _, frame = cam.read() + img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + img = cv2.GaussianBlur(img, (3, 3), 0) + # Все больше 215 станет 255, все что меньше 0 + T, thresh_img = cv2.threshold(img, 215, 255, cv2.THRESH_BINARY) + + + cv2.imshow("Image", img) + cv2.imshow("Image with opening", thresh_img) + #cv2.imshow("Image with closing", res) + + k = cv2.waitKey(2) + if k == 27: + cv2.destroyAllWindows() + break \ No newline at end of file