{"id":333706,"date":"2022-05-27T09:00:23","date_gmt":"2022-05-27T09:00:23","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=333706"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=333706","title":{"rendered":"<span>\u041f\u043e\u0434\u0433\u043e\u043d \u043f\u043e\u0434 MNIST-\u043e\u0432\u0441\u043a\u0438\u0439 \u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 1000 \u0438 1 \u0441\u0442\u0430\u0442\u044c\u044e \u043f\u043e \u0442\u0440\u0435\u043d\u0438\u043d\u0433\u0443 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u043e\u0433\u043e \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u0440\u0443\u043a\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b. \u041e\u0434\u043d\u0430\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0434\u0435\u043b\u043e \u0434\u043e\u0445\u043e\u0434\u0438\u0442 \u0434\u043e \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0448\u044c \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u0442\u043e \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043b\u043e\u0445\u043e \u0438\u043b\u0438 \u043d\u0435 \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0432\u0441\u0435. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u043e\u0442\u0442\u0435\u043d\u043a\u0438 \u0441\u0435\u0440\u043e\u0433\u043e, \u043d\u0430\u0441\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e\u0434 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u0438\u0439 \u043d\u0430 28&#215;28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u0438 \u0442\u043e\u0433\u0434\u0430 \u043d\u0430\u0448\u0430 \u0441\u0435\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438: <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/768\/a7e\/cec\/768a7ecec8d9bfb4be97381f288659b5.png\" width=\"910\" height=\"793\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/768\/a7e\/cec\/768a7ecec8d9bfb4be97381f288659b5.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0431\u0430\u0437\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a MNIST. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 MNIST-\u043e\u0432\u0441\u043a\u0438\u0435 \u0446\u0438\u0444\u0440\u044b \u043f\u043e\u043c\u0435\u0449\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 20&#215;20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439. \u0417\u0430\u0442\u0435\u043c \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u043e\u043d\u043e \u0440\u0430\u0441\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u043b\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28&#215;28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u043b \u0441 \u0446\u0435\u043d\u0442\u0440\u043e\u043c \u043f\u043e\u043b\u044f. \u0418\u043c\u0435\u043d\u043d\u043e \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0432\u0438\u0434\u0443 \u043c\u044b \u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0434\u0433\u043e\u043d\u044f\u0442\u044c \u043d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043b\u044e\u0431\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u043e\u0434\u0435\u043b\u044c\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f MNIST-\u043e\u0432\u0441\u043a\u0438\u0445 \u0447\u0438\u0441\u0435\u043b. <a href=\"https:\/\/machinelearningmastery.com\/how-to-develop-a-convolutional-neural-network-from-scratch-for-mnist-handwritten-digit-classification\/\" rel=\"noopener noreferrer nofollow\">\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440<\/a>:<\/p>\n<pre><code class=\"python\">from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D from tensorflow.keras.layers import MaxPooling2D from tensorflow.keras.layers import Dense from tensorflow.keras.layers import Flatten from tensorflow.keras.optimizers import SGD   # load train and test dataset def load_dataset():   # load dataset   (trainX, trainY), (testX, testY) = mnist.load_data()   # reshape dataset to have a single channel   trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))   testX = testX.reshape((testX.shape[0], 28, 28, 1))   # one hot encode target values   trainY = to_categorical(trainY)   testY = to_categorical(testY)   return trainX, trainY, testX, testY   # scale pixels def prep_pixels(train, test):   # convert from integers to floats   train_norm = train.astype('float32')   test_norm = test.astype('float32')   # normalize to range 0-1   train_norm = train_norm \/ 255.0   test_norm = test_norm \/ 255.0   # return normalized images   return train_norm, test_norm   # define cnn model def define_model():   model = Sequential()   model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', input_shape=(28, 28, 1)))   model.add(MaxPooling2D((2, 2)))   model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'))   model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'))   model.add(MaxPooling2D((2, 2)))   model.add(Flatten())   model.add(Dense(100, activation='relu', kernel_initializer='he_uniform'))   model.add(Dense(10, activation='softmax'))   # compile model   opt = SGD(learning_rate=0.01, momentum=0.9)   model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])   return model   # run the test harness for evaluating a model def run_test_harness():   # load dataset   trainX, trainY, testX, testY = load_dataset()   # prepare pixel data   trainX, testX = prep_pixels(trainX, testX)   # define model   model = define_model()   # fit model   model.fit(trainX, trainY, epochs=10, batch_size=32, verbose=1)   # save model   model.save('digit_model.h5')   _, acc = model.evaluate(testX, testY, verbose=0)   print('> %.3f' % (acc * 100.0))  # entry point, run the test harness run_test_harness()  >>> 99.040<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0439 accuracy. \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043d\u0430\u0448\u0438 \u043b\u0438\u0447\u043d\u044b\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0435\u0442\u044c. \u0421\u0430\u043c\u044b\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u0435\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0438\u043d\u0433\u0430: \u0437\u0430\u0441\u043a\u0435\u0439\u043b\u0438\u0442\u044c \u0434\u043e 28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0446\u0432\u0435\u0442:<\/p>\n<pre><code class=\"python\">import cv2 import numpy as np  def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255 - img      gray = cv2.resize(gray, (28, 28))   cv2.imwrite('gray'+ img_path, gray)   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/07d\/bd7\/02f\/07dbd702f82f03a217a6569f112ab071.JPG\" width=\"630\" height=\"286\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041d\u043e\u043b\u044c \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043b\u0441\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u0438 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u0432 \u0446\u0435\u043b\u043e\u043c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0443\u0434\u0430\u0447\u043d\u043e. \u0421 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0447\u0438\u0441\u043b\u0430\u043c\u0438 \u043f\u043b\u043e\u0445\u043e. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043d\u0430 5 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445 \u0432\u0441\u0435\u0433\u043e 20 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432. <\/p>\n<p>\u0415\u0449\u0435 \u0440\u0430\u0437 \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u043c \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0442\u0435\u0437\u0438\u0441, \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 <a href=\"http:\/\/yann.lecun.com\/exdb\/mnist\/\" rel=\"noopener noreferrer nofollow\">\u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/a>: The original black and white (bilevel) images from NIST were size normalized to fit in a 20&#215;20 pixel box while preserving their aspect ratio. The resulting images contain grey levels as a result of the anti-aliasing technique used by the normalization algorithm. the images were centered in a 28&#215;28 image by computing the center of mass of the pixels, and translating the image so as to position this point at the center of the 28&#215;28 field.<\/p>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0432\u0441\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0444\u043e\u0440\u043c\u0430\u0442\u0443. \u0422\u0430\u043a\u0436\u0435 \u043e\u0442\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0444\u043e\u043d \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0431\u0435\u043b\u044b\u0439, \u0442\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0447\u0442\u043e-\u0442\u043e, \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0449\u0435\u0435\u0441\u044f \u043e\u0442 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u043e\u0433\u043e \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430, \u0431\u0435\u043b\u0430\u044f \u0446\u0438\u0444\u0440\u0430 \u043d\u0430 \u0447\u0435\u0440\u043d\u043e\u043c \u0444\u043e\u043d\u0435, \u043a\u0430\u043a \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441 \u0434\u0435\u0432\u044f\u0442\u043a\u043e\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043f\u043e\u0441\u043b\u0435 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\">def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      gray = cv2.resize(gray, (28, 28))   cv2.imwrite('gray'+ img_path, gray)   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d97\/e1c\/1b6\/d97e1c1b6876a3e2b6e68ac6a654dd36.JPG\" alt=\"\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\" title=\"\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\" width=\"263\" height=\"246\"\/><figcaption>\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/figcaption><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u0431\u043e\u043a\u0441 20&#215;20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439. \u041c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438. \u041e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 &#8212; \u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043d\u0442\u0443\u0440, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0439 \u0446\u0438\u0444\u0440\u0443, \u0432\u0437\u044f\u0442\u044c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c <strong>resize <\/strong>\u0434\u043e \u043d\u0443\u0436\u043d\u044b\u0445 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432. <a href=\"https:\/\/stackoverflow.com\/questions\/21104664\/extract-all-bounding-boxes-using-opencv-python\" rel=\"noopener noreferrer nofollow\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a>, \u043a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c. \u0412 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f, \u0435\u0441\u043b\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u043b\u0430 \u0438\u0437 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043e\u0434\u043d\u043e\u0439 \u0446\u0438\u0444\u0440\u044b. <\/p>\n<p>\u041c\u044b \u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0449\u0435 \u0438, \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435. \u0410 \u0438\u043c\u0435\u043d\u043d\u043e, \u0441\u043f\u0435\u0440\u0432\u0430 \u0443\u0434\u0430\u043b\u0438\u043c \u0432\u0441\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0438\u043a\u0441\u0435\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u043d\u044b\u0435. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443, \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u044f\u0432\u043b\u044f\u044e\u0449\u0435\u0439\u0441\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u043e\u043b\u043e\u0447\u043a\u043e\u0439 \u043d\u0430\u0448\u0435\u0439 \u0446\u0438\u0444\u0440\u044b.<\/p>\n<pre><code class=\"python\">def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      # \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b   while np.sum(gray[0]) == 0:     gray = gray[1:]   while np.sum(gray[:,0]) == 0:     gray = np.delete(gray,0,1)   while np.sum(gray[-1]) == 0:     gray = gray[:-1]   while np.sum(gray[:,-1]) == 0:     gray = np.delete(gray,-1,1)   rows, \u0441ols = gray.shape      cv2.imwrite('gray'+ img_path, gray)   gray = cv2.resize(gray, (28, 28))   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/db6\/52f\/997\/db652f9973e32b549b1121acdf02b2ae.JPG\" alt=\"\u0412 \u0446\u0435\u043b\u043e\u043c, \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f bounding boxes.\" title=\"\u0412 \u0446\u0435\u043b\u043e\u043c, \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f bounding boxes.\" width=\"613\" height=\"169\"\/><figcaption>\u0412 \u0446\u0435\u043b\u043e\u043c, \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f bounding boxes.<\/figcaption><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043f\u043e\u043c\u0435\u0449\u0430\u043b\u0438\u0441\u044c \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 20&#215;20.  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u043a\u0442\u043e\u0440, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u0434\u043b\u0438\u043d\u043e\u0439 \u0432 20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439:<\/p>\n<pre><code class=\"python\">def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      # \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b   while np.sum(gray[0]) == 0:     gray = gray[1:]   while np.sum(gray[:,0]) == 0:     gray = np.delete(gray,0,1)   while np.sum(gray[-1]) == 0:     gray = gray[:-1]   while np.sum(gray[:,-1]) == 0:     gray = np.delete(gray,-1,1)   rows, \u0441ols = gray.shape      # \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u0435\u0449\u0430\u043b\u043e\u0441\u044c \u0432 box 20x20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439   if rows > cols:     factor = 20.0\/rows     rows = 20     cols = int(round(cols*factor))     gray = cv2.resize(gray, (cols,rows))   else:     factor = 20.0\/cols     cols = 20     rows = int(round(rows*factor))     gray = cv2.resize(gray, (cols, rows))      cv2.imwrite('gray'+ img_path, gray)   gray = cv2.resize(gray, (28, 28))   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0434\u043e 28&#215;28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u0447\u0435\u0440\u043d\u044b\u0435 \u0440\u044f\u0434\u044b \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u043f\u043e \u043a\u0440\u0430\u044f\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>np.lib.pad<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0443\u043b\u0438 \u043f\u043e \u043a\u0440\u0430\u044f\u043c. \u0418 \u0441\u0440\u0430\u0437\u0443 \u0443\u0434\u0430\u043b\u0438\u043c \u0441\u0442\u0440\u043e\u0447\u043a\u0443 <code>gray = cv2.resize(gray, (28, 28)).<\/code> \u041f\u043e\u0441\u043b\u0435 \u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c:<\/p>\n<pre><code class=\"python\">colsPadding = (int(math.ceil((28-cols)\/2.0)),int(math.floor((28-cols)\/2.0))) rowsPadding = (int(math.ceil((28-rows)\/2.0)),int(math.floor((28-rows)\/2.0))) gray = np.lib.pad(gray,(rowsPadding,colsPadding),'constant')<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/67b\/a26\/957\/67ba2695749dd4fac0f1d65e380d0a00.JPG\" alt=\"\u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28x28\" title=\"\u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28x28\" width=\"602\" height=\"135\"\/><figcaption>\u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28&#215;28<\/figcaption><\/figure>\n<p>\u0412 \u0446\u0435\u043b\u043e\u043c, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0443\u0436\u0435 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0448\u0430\u0433 &#8212; \u043f\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043a\u043e\u0440\u043e\u0431\u043a\u0443 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0435\u0435 \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u043b \u0441 \u0446\u0435\u043d\u0442\u0440\u043e\u043c \u0432\u0441\u0435\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u0417\u0430\u0432\u0435\u0434\u0435\u043c \u0434\u0432\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041f\u0435\u0440\u0432\u0430\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0434\u0432\u0438\u0433\u0430:<\/p>\n<pre><code class=\"python\">from scipy.ndimage.measurements import center_of_mass def getBestShift(img):     cy,cx = center_of_mass(img)      rows,cols = img.shape     shiftx = np.round(cols\/2.0-cx).astype(int)     shifty = np.round(rows\/2.0-cy).astype(int)      return shiftx,shifty<\/code><\/pre>\n<p>\u0418 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0434\u0432\u0438\u0433\u0430\u0435\u0442 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u043d\u0443\u0436\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e <a href=\"https:\/\/docs.opencv.org\/4.x\/da\/d6e\/tutorial_py_geometric_transformations.html#gsc.tab=0\" rel=\"noopener noreferrer nofollow\">warpAffine<\/a>. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043c\u0430\u0442\u0440\u0438\u0446\u0430 \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/baf\/d8a\/dde\/bafd8addecb49337275ec66448e0b26d.png\" width=\"174\" height=\"54\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/baf\/d8a\/dde\/bafd8addecb49337275ec66448e0b26d.png\"\/><figcaption><\/figcaption><\/figure>\n<pre><code class=\"python\">def shift(img,sx,sy):     rows,cols = img.shape     M = np.float32([[1,0,sx],[0,1,sy]])     shifted = cv2.warpAffine(img,M,(cols,rows))     return shifted<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u0441\u0442\u0440\u043e\u0447\u0435\u043a \u0441\u043e \u0441\u0434\u0432\u0438\u0433\u043e\u043c \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441:<\/p>\n<pre><code class=\"python\">shiftx,shifty = getBestShift(gray) shifted = shift(gray,shiftx,shifty) gray = shifted<\/code><\/pre>\n<p>\u0418 \u043f\u043e \u0438\u0442\u043e\u0433\u0443 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0434\u0433\u043e\u043d \u043f\u043e\u0434 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u0438\u0439 \u0434\u0430\u0442\u0430\u0441\u0435\u0442:<\/p>\n<pre><code class=\"python\">from scipy.ndimage.measurements import center_of_mass import math  import cv2 import numpy as np  def getBestShift(img):     cy,cx = center_of_mass(img)          rows,cols = img.shape     shiftx = np.round(cols\/2.0-cx).astype(int)     shifty = np.round(rows\/2.0-cy).astype(int)      return shiftx,shifty    def shift(img,sx,sy):     rows,cols = img.shape     M = np.float32([[1,0,sx],[0,1,sy]])     shifted = cv2.warpAffine(img,M,(cols,rows))     return shifted    def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      # \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b   while np.sum(gray[0]) == 0:     gray = gray[1:]   while np.sum(gray[:,0]) == 0:     gray = np.delete(gray,0,1)   while np.sum(gray[-1]) == 0:     gray = gray[:-1]   while np.sum(gray[:,-1]) == 0:     gray = np.delete(gray,-1,1)   rows,cols = gray.shape      # \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u0435\u0449\u0430\u043b\u043e\u0441\u044c \u0432 box 20x20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439   if rows > cols:     factor = 20.0\/rows     rows = 20     cols = int(round(cols*factor))     gray = cv2.resize(gray, (cols,rows))   else:     factor = 20.0\/cols     cols = 20     rows = int(round(rows*factor))     gray = cv2.resize(gray, (cols, rows))    # \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28x28   colsPadding = (int(math.ceil((28-cols)\/2.0)),int(math.floor((28-cols)\/2.0)))   rowsPadding = (int(math.ceil((28-rows)\/2.0)),int(math.floor((28-rows)\/2.0)))   gray = np.lib.pad(gray,(rowsPadding,colsPadding),'constant')    # \u0441\u0434\u0432\u0438\u0433\u0430\u0435\u043c \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441   shiftx,shifty = getBestShift(gray)   shifted = shift(gray,shiftx,shifty)   gray = shifted      cv2.imwrite('gray'+ img_path, gray)   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0437\u0430\u0434\u0430\u0442\u044c\u0441\u044f \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u043c, \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043b\u0438 \u0441\u0434\u0432\u0438\u0433 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441 \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u043e\u0431\u0449\u0435 \u0445\u043e\u0442\u044c \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0442\u043e\u043b\u043a, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 20&#215;20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439? \u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u0431\u0443\u0434\u0435\u0442, \u043f\u0443\u0441\u0442\u044c \u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u043c\u044b \u043f\u043e\u0434\u043e\u0433\u043d\u0430\u043b\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043f\u043e\u0434 MNIST-\u043e\u0432\u0441\u043a\u0438\u0439 \u0434\u0430\u0442\u0430\u0441\u0435\u0442.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e4e\/528\/659\/e4e52865967cad3b44fbcfd48cf4c08e.JPG\" alt=\"\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043e \u0441\u0434\u0432\u0438\u0433\u043e\u043c \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441\" title=\"\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043e \u0441\u0434\u0432\u0438\u0433\u043e\u043c \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441\" width=\"630\" height=\"152\"\/><figcaption>\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043e \u0441\u0434\u0432\u0438\u0433\u043e\u043c \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441<\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/69f\/e9d\/f37\/69fe9df37908b254ad9eaa5fba6eaaa1.JPG\" alt=\"\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0441\u0434\u0432\u0438\u0433\u0430 \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441, \u043f\u043e\u0441\u043b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0433\u0440\u0430\u043d\u0438\u0446 \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28x28\" title=\"\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0441\u0434\u0432\u0438\u0433\u0430 \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441, \u043f\u043e\u0441\u043b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0433\u0440\u0430\u043d\u0438\u0446 \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28x28\" width=\"602\" height=\"135\"\/><figcaption>\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0441\u0434\u0432\u0438\u0433\u0430 \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441, \u043f\u043e\u0441\u043b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0433\u0440\u0430\u043d\u0438\u0446 \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28&#215;28<\/figcaption><\/figure>\n<p>\u041a\u0430\u043a \u0438\u0442\u043e\u0433, \u043c\u043e\u0434\u0435\u043b\u044c\u043a\u0430 \u0432\u044b\u0448\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0438\u043d\u0433\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0434\u0430\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ee7\/3c8\/c6b\/ee73c8c6be6879a40c8a612fbb5a9b3d.JPG\" width=\"626\" height=\"304\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u043e\u0441\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0434\u043b\u044f <a href=\"https:\/\/github.com\/spbu-math-cs\/ml-course\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/spbu-math-cs\/ml-course<\/a>  <\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/668144\/\"> https:\/\/habr.com\/ru\/post\/668144\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 1000 \u0438 1 \u0441\u0442\u0430\u0442\u044c\u044e \u043f\u043e \u0442\u0440\u0435\u043d\u0438\u043d\u0433\u0443 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u043e\u0433\u043e \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u0440\u0443\u043a\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b. \u041e\u0434\u043d\u0430\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0434\u0435\u043b\u043e \u0434\u043e\u0445\u043e\u0434\u0438\u0442 \u0434\u043e \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0448\u044c \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u0442\u043e \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043b\u043e\u0445\u043e \u0438\u043b\u0438 \u043d\u0435 \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0432\u0441\u0435. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u043e\u0442\u0442\u0435\u043d\u043a\u0438 \u0441\u0435\u0440\u043e\u0433\u043e, \u043d\u0430\u0441\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e\u0434 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u0438\u0439 \u043d\u0430 28&#215;28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u0438 \u0442\u043e\u0433\u0434\u0430 \u043d\u0430\u0448\u0430 \u0441\u0435\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438: <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0431\u0430\u0437\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a MNIST. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 MNIST-\u043e\u0432\u0441\u043a\u0438\u0435 \u0446\u0438\u0444\u0440\u044b \u043f\u043e\u043c\u0435\u0449\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 20&#215;20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439. \u0417\u0430\u0442\u0435\u043c \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u043e\u043d\u043e \u0440\u0430\u0441\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u043b\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28&#215;28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u043b \u0441 \u0446\u0435\u043d\u0442\u0440\u043e\u043c \u043f\u043e\u043b\u044f. \u0418\u043c\u0435\u043d\u043d\u043e \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0432\u0438\u0434\u0443 \u043c\u044b \u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0434\u0433\u043e\u043d\u044f\u0442\u044c \u043d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043b\u044e\u0431\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u043e\u0434\u0435\u043b\u044c\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f MNIST-\u043e\u0432\u0441\u043a\u0438\u0445 \u0447\u0438\u0441\u0435\u043b. <a href=\"https:\/\/machinelearningmastery.com\/how-to-develop-a-convolutional-neural-network-from-scratch-for-mnist-handwritten-digit-classification\/\" rel=\"noopener noreferrer nofollow\">\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440<\/a>:<\/p>\n<pre><code class=\"python\">from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D from tensorflow.keras.layers import MaxPooling2D from tensorflow.keras.layers import Dense from tensorflow.keras.layers import Flatten from tensorflow.keras.optimizers import SGD   # load train and test dataset def load_dataset():   # load dataset   (trainX, trainY), (testX, testY) = mnist.load_data()   # reshape dataset to have a single channel   trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))   testX = testX.reshape((testX.shape[0], 28, 28, 1))   # one hot encode target values   trainY = to_categorical(trainY)   testY = to_categorical(testY)   return trainX, trainY, testX, testY   # scale pixels def prep_pixels(train, test):   # convert from integers to floats   train_norm = train.astype('float32')   test_norm = test.astype('float32')   # normalize to range 0-1   train_norm = train_norm \/ 255.0   test_norm = test_norm \/ 255.0   # return normalized images   return train_norm, test_norm   # define cnn model def define_model():   model = Sequential()   model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', input_shape=(28, 28, 1)))   model.add(MaxPooling2D((2, 2)))   model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'))   model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'))   model.add(MaxPooling2D((2, 2)))   model.add(Flatten())   model.add(Dense(100, activation='relu', kernel_initializer='he_uniform'))   model.add(Dense(10, activation='softmax'))   # compile model   opt = SGD(learning_rate=0.01, momentum=0.9)   model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])   return model   # run the test harness for evaluating a model def run_test_harness():   # load dataset   trainX, trainY, testX, testY = load_dataset()   # prepare pixel data   trainX, testX = prep_pixels(trainX, testX)   # define model   model = define_model()   # fit model   model.fit(trainX, trainY, epochs=10, batch_size=32, verbose=1)   # save model   model.save('digit_model.h5')   _, acc = model.evaluate(testX, testY, verbose=0)   print('> %.3f' % (acc * 100.0))  # entry point, run the test harness run_test_harness()  >>> 99.040<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0439 accuracy. \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043d\u0430\u0448\u0438 \u043b\u0438\u0447\u043d\u044b\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0435\u0442\u044c. \u0421\u0430\u043c\u044b\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u0435\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0438\u043d\u0433\u0430: \u0437\u0430\u0441\u043a\u0435\u0439\u043b\u0438\u0442\u044c \u0434\u043e 28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0446\u0432\u0435\u0442:<\/p>\n<pre><code class=\"python\">import cv2 import numpy as np  def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255 - img      gray = cv2.resize(gray, (28, 28))   cv2.imwrite('gray'+ img_path, gray)   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041d\u043e\u043b\u044c \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043b\u0441\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u0438 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u0432 \u0446\u0435\u043b\u043e\u043c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0443\u0434\u0430\u0447\u043d\u043e. \u0421 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0447\u0438\u0441\u043b\u0430\u043c\u0438 \u043f\u043b\u043e\u0445\u043e. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043d\u0430 5 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445 \u0432\u0441\u0435\u0433\u043e 20 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432. <\/p>\n<p>\u0415\u0449\u0435 \u0440\u0430\u0437 \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u043c \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0442\u0435\u0437\u0438\u0441, \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 <a href=\"http:\/\/yann.lecun.com\/exdb\/mnist\/\" rel=\"noopener noreferrer nofollow\">\u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/a>: The original black and white (bilevel) images from NIST were size normalized to fit in a 20&#215;20 pixel box while preserving their aspect ratio. The resulting images contain grey levels as a result of the anti-aliasing technique used by the normalization algorithm. the images were centered in a 28&#215;28 image by computing the center of mass of the pixels, and translating the image so as to position this point at the center of the 28&#215;28 field.<\/p>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0432\u0441\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0444\u043e\u0440\u043c\u0430\u0442\u0443. \u0422\u0430\u043a\u0436\u0435 \u043e\u0442\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0444\u043e\u043d \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0431\u0435\u043b\u044b\u0439, \u0442\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0447\u0442\u043e-\u0442\u043e, \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0449\u0435\u0435\u0441\u044f \u043e\u0442 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u043e\u0433\u043e \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430, \u0431\u0435\u043b\u0430\u044f \u0446\u0438\u0444\u0440\u0430 \u043d\u0430 \u0447\u0435\u0440\u043d\u043e\u043c \u0444\u043e\u043d\u0435, \u043a\u0430\u043a \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441 \u0434\u0435\u0432\u044f\u0442\u043a\u043e\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043f\u043e\u0441\u043b\u0435 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\">def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      gray = cv2.resize(gray, (28, 28))   cv2.imwrite('gray'+ img_path, gray)   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<figure class=\"\"><figcaption>\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/figcaption><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u0431\u043e\u043a\u0441 20&#215;20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439. \u041c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438. \u041e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 &#8212; \u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043d\u0442\u0443\u0440, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0439 \u0446\u0438\u0444\u0440\u0443, \u0432\u0437\u044f\u0442\u044c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c <strong>resize <\/strong>\u0434\u043e \u043d\u0443\u0436\u043d\u044b\u0445 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432. <a href=\"https:\/\/stackoverflow.com\/questions\/21104664\/extract-all-bounding-boxes-using-opencv-python\" rel=\"noopener noreferrer nofollow\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a>, \u043a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c. \u0412 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f, \u0435\u0441\u043b\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u043b\u0430 \u0438\u0437 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043e\u0434\u043d\u043e\u0439 \u0446\u0438\u0444\u0440\u044b. <\/p>\n<p>\u041c\u044b \u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0449\u0435 \u0438, \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435. \u0410 \u0438\u043c\u0435\u043d\u043d\u043e, \u0441\u043f\u0435\u0440\u0432\u0430 \u0443\u0434\u0430\u043b\u0438\u043c \u0432\u0441\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0438\u043a\u0441\u0435\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u043d\u044b\u0435. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443, \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u044f\u0432\u043b\u044f\u044e\u0449\u0435\u0439\u0441\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u043e\u043b\u043e\u0447\u043a\u043e\u0439 \u043d\u0430\u0448\u0435\u0439 \u0446\u0438\u0444\u0440\u044b.<\/p>\n<pre><code class=\"python\">def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      # \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b   while np.sum(gray[0]) == 0:     gray = gray[1:]   while np.sum(gray[:,0]) == 0:     gray = np.delete(gray,0,1)   while np.sum(gray[-1]) == 0:     gray = gray[:-1]   while np.sum(gray[:,-1]) == 0:     gray = np.delete(gray,-1,1)   rows, \u0441ols = gray.shape      cv2.imwrite('gray'+ img_path, gray)   gray = cv2.resize(gray, (28, 28))   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<figure class=\"full-width\"><figcaption>\u0412 \u0446\u0435\u043b\u043e\u043c, \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f bounding boxes.<\/figcaption><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043f\u043e\u043c\u0435\u0449\u0430\u043b\u0438\u0441\u044c \u0432 \u043a\u0432\u0430\u0434\u0440\u0430\u0442 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 20&#215;20.  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u043a\u0442\u043e\u0440, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u0434\u043b\u0438\u043d\u043e\u0439 \u0432 20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439:<\/p>\n<pre><code class=\"python\">def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      # \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b   while np.sum(gray[0]) == 0:     gray = gray[1:]   while np.sum(gray[:,0]) == 0:     gray = np.delete(gray,0,1)   while np.sum(gray[-1]) == 0:     gray = gray[:-1]   while np.sum(gray[:,-1]) == 0:     gray = np.delete(gray,-1,1)   rows, \u0441ols = gray.shape      # \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u0435\u0449\u0430\u043b\u043e\u0441\u044c \u0432 box 20x20 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439   if rows > cols:     factor = 20.0\/rows     rows = 20     cols = int(round(cols*factor))     gray = cv2.resize(gray, (cols,rows))   else:     factor = 20.0\/cols     cols = 20     rows = int(round(rows*factor))     gray = cv2.resize(gray, (cols, rows))      cv2.imwrite('gray'+ img_path, gray)   gray = cv2.resize(gray, (28, 28))   img = gray \/ 255.0   img = np.array(img).reshape(-1, 28, 28, 1)   out = str(np.argmax(model.predict(img)))   return out<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0434\u043e 28&#215;28 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u0447\u0435\u0440\u043d\u044b\u0435 \u0440\u044f\u0434\u044b \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u043f\u043e \u043a\u0440\u0430\u044f\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>np.lib.pad<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0443\u043b\u0438 \u043f\u043e \u043a\u0440\u0430\u044f\u043c. \u0418 \u0441\u0440\u0430\u0437\u0443 \u0443\u0434\u0430\u043b\u0438\u043c \u0441\u0442\u0440\u043e\u0447\u043a\u0443 <code>gray = cv2.resize(gray, (28, 28)).<\/code> \u041f\u043e\u0441\u043b\u0435 \u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c:<\/p>\n<pre><code class=\"python\">colsPadding = (int(math.ceil((28-cols)\/2.0)),int(math.floor((28-cols)\/2.0))) rowsPadding = (int(math.ceil((28-rows)\/2.0)),int(math.floor((28-rows)\/2.0))) gray = np.lib.pad(gray,(rowsPadding,colsPadding),'constant')<\/code><\/pre>\n<figure class=\"full-width\"><figcaption>\u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 28&#215;28<\/figcaption><\/figure>\n<p>\u0412 \u0446\u0435\u043b\u043e\u043c, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0443\u0436\u0435 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0448\u0430\u0433 &#8212; \u043f\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043a\u043e\u0440\u043e\u0431\u043a\u0443 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0435\u0435 \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u043b \u0441 \u0446\u0435\u043d\u0442\u0440\u043e\u043c \u0432\u0441\u0435\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u0417\u0430\u0432\u0435\u0434\u0435\u043c \u0434\u0432\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041f\u0435\u0440\u0432\u0430\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u0446\u0435\u043d\u0442\u0440 \u043c\u0430\u0441\u0441 \u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0434\u0432\u0438\u0433\u0430:<\/p>\n<pre><code class=\"python\">from scipy.ndimage.measurements import center_of_mass def getBestShift(img):     cy,cx = center_of_mass(img)      rows,cols = img.shape     shiftx = np.round(cols\/2.0-cx).astype(int)     shifty = np.round(rows\/2.0-cy).astype(int)      return shiftx,shifty<\/code><\/pre>\n<p>\u0418 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0434\u0432\u0438\u0433\u0430\u0435\u0442 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0432 \u043d\u0443\u0436\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e <a href=\"https:\/\/docs.opencv.org\/4.x\/da\/d6e\/tutorial_py_geometric_transformations.html#gsc.tab=0\" rel=\"noopener noreferrer nofollow\">warpAffine<\/a>. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043c\u0430\u0442\u0440\u0438\u0446\u0430 \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438:<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<pre><code class=\"python\">def shift(img,sx,sy):     rows,cols = img.shape     M = np.float32([[1,0,sx],[0,1,sy]])     shifted = cv2.warpAffine(img,M,(cols,rows))     return shifted<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u0441\u0442\u0440\u043e\u0447\u0435\u043a \u0441\u043e \u0441\u0434\u0432\u0438\u0433\u043e\u043c \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0446\u0435\u043d\u0442\u0440\u0430 \u043c\u0430\u0441\u0441:<\/p>\n<pre><code class=\"python\">shiftx,shifty = getBestShift(gray) shifted = shift(gray,shiftx,shifty) gray = shifted<\/code><\/pre>\n<p>\u0418 \u043f\u043e \u0438\u0442\u043e\u0433\u0443 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0434\u0433\u043e\u043d \u043f\u043e\u0434 \u043c\u043d\u0438\u0441\u0442\u043e\u0432\u0441\u043a\u0438\u0439 \u0434\u0430\u0442\u0430\u0441\u0435\u0442:<\/p>\n<pre><code class=\"python\">from scipy.ndimage.measurements import center_of_mass import math  import cv2 import numpy as np  def getBestShift(img):     cy,cx = center_of_mass(img)          rows,cols = img.shape     shiftx = np.round(cols\/2.0-cx).astype(int)     shifty = np.round(rows\/2.0-cy).astype(int)      return shiftx,shifty    def shift(img,sx,sy):     rows,cols = img.shape     M = np.float32([[1,0,sx],[0,1,sy]])     shifted = cv2.warpAffine(img,M,(cols,rows))     return shifted    def rec_digit(img_path):   img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)   gray = 255-img   # \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443   (thresh, gray) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)      # \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u044b   while<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-333706","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/333706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=333706"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/333706\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=333706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=333706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=333706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}