# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + [markdown] id="view-in-github" colab_type="text"
# <a href="https://colab.research.google.com/github/fatemetkl/Persian-Handwritten-Image-Recognition/blob/master/FarsiNumbers-CV.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

# + id="1YiDZHqc5Qta" colab_type="code" outputId="7ed8d0be-d933-47fe-a32a-2910e6573c5d" colab={"base_uri": "https://localhost:8080/", "height": 124}
from google.colab import drive
drive.mount('/content/gdrive')

# + id="lbFJHkBB7D2f" colab_type="code" outputId="95ec3c7c-7e25-41a8-8760-5a08bda3d8e1" colab={"base_uri": "https://localhost:8080/", "height": 87}
# ! git clone https://github.com/amir-saniyan/HodaDatasetReader.git

# + id="BbZ-9uXy9hWs" colab_type="code" colab={}
# *-* coding: utf-8 *-*

# Hoda Dataset Reader
# Python code for reading Hoda farsi digit dataset.

# Hoda Farsi Digit Dataset:
# http://farsiocr.ir/
# http://farsiocr.ir/مجموعه-داده/مجموعه-ارقام-دستنویس-هدی
# http://dadegan.ir/catalog/hoda

# Repository:
# https://github.com/amir-saniyan/HodaDatasetReader

import struct
import numpy as np
import cv2


def __convert_to_one_hot(vector, num_classes):
    result = np.zeros(shape=[len(vector), num_classes])
    result[np.arange(len(vector)), vector] = 1
    return result


def __resize_image(src_image, dst_image_height, dst_image_width):
    src_image_height = src_image.shape[0]
    src_image_width = src_image.shape[1]

    if src_image_height > dst_image_height or src_image_width > dst_image_width:
        height_scale = dst_image_height / src_image_height
        width_scale = dst_image_width / src_image_width
        scale = min(height_scale, width_scale)
        img = cv2.resize(src=src_image, dsize=(0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
    else:
        img = src_image

    img_height = img.shape[0]
    img_width = img.shape[1]

    dst_image = np.zeros(shape=[dst_image_height, dst_image_width], dtype=np.uint8)

    y_offset = (dst_image_height - img_height) // 2
    x_offset = (dst_image_width - img_width) // 2

    dst_image[y_offset:y_offset+img_height, x_offset:x_offset+img_width] = img

    return dst_image


def read_hoda_cdb(file_name):
    with open(file_name, 'rb') as binary_file:

        data = binary_file.read()

        offset = 0

        # read private header

        yy = struct.unpack_from('H', data, offset)[0]
        offset += 2

        m = struct.unpack_from('B', data, offset)[0]
        offset += 1

        d = struct.unpack_from('B', data, offset)[0]
        offset += 1

        H = struct.unpack_from('B', data, offset)[0]
        offset += 1

        W = struct.unpack_from('B', data, offset)[0]
        offset += 1

        TotalRec = struct.unpack_from('I', data, offset)[0]
        offset += 4

        LetterCount = struct.unpack_from('128I', data, offset)
        offset += 128 * 4

        imgType = struct.unpack_from('B', data, offset)[0]  # 0: binary, 1: gray
        offset += 1

        Comments = struct.unpack_from('256c', data, offset)
        offset += 256 * 1

        Reserved = struct.unpack_from('245c', data, offset)
        offset += 245 * 1

        if (W > 0) and (H > 0):
            normal = True
        else:
            normal = False

        images = []
        labels = []

        for i in range(TotalRec):

            StartByte = struct.unpack_from('B', data, offset)[0]  # must be 0xff
            offset += 1

            label = struct.unpack_from('B', data, offset)[0]
            offset += 1

            if not normal:
                W = struct.unpack_from('B', data, offset)[0]
                offset += 1

                H = struct.unpack_from('B', data, offset)[0]
                offset += 1

            ByteCount = struct.unpack_from('H', data, offset)[0]
            offset += 2

            image = np.zeros(shape=[H, W], dtype=np.uint8)

            if imgType == 0:
                # Binary
                for y in range(H):
                    bWhite = True
                    counter = 0
                    while counter < W:
                        WBcount = struct.unpack_from('B', data, offset)[0]
                        offset += 1
                        # x = 0
                        # while x < WBcount:
                        #     if bWhite:
                        #         image[y, x + counter] = 0  # Background
                        #     else:
                        #         image[y, x + counter] = 255  # ForeGround
                        #     x += 1
                        if bWhite:
                            image[y, counter:counter + WBcount] = 0  # Background
                        else:
                            image[y, counter:counter + WBcount] = 255  # ForeGround
                        bWhite = not bWhite  # black white black white ...
                        counter += WBcount
            else:
                # GrayScale mode
                data = struct.unpack_from('{}B'.format(W * H), data, offset)
                offset += W * H
                image = np.asarray(data, dtype=np.uint8).reshape([W, H]).T

            images.append(image)
            labels.append(label)

        return images, labels


def read_hoda_dataset(dataset_path, images_height=32, images_width=32, one_hot=False, reshape=True):
    images, labels = read_hoda_cdb(dataset_path)
    assert len(images) == len(labels)

    X = np.zeros(shape=[len(images), images_height, images_width], dtype=np.float32)
    Y = np.zeros(shape=[len(labels)], dtype=np.int)

    for i in range(len(images)):
        image = images[i]
        # Image resizing.
        image = __resize_image(src_image=image, dst_image_height=images_height, dst_image_width=images_width)
        # Image normalization.
        image = image / 255
        # Image binarization.
        image = np.where(image >= 0.5, 1, 0)
        # Image.
        X[i] = image
        # Label.
        Y[i] = labels[i]

    if one_hot:
        Y = __convert_to_one_hot(Y, 10).astype(dtype=np.float32)
    else:
        Y = Y.astype(dtype=np.float32)

    if reshape:
        X = X.reshape(-1, images_height * images_width)
    else:
        X = X.reshape(-1, images_height, images_width, 1)

    return X, Y



# + id="V3bR9oyF--0j" colab_type="code" outputId="40788b85-31ba-4127-a389-4e3f2db33dd2" colab={"base_uri": "https://localhost:8080/", "height": 35}
# !cd HodaDatasetReader
# !ls

# + id="vzEXO4kn91CH" colab_type="code" outputId="f3feaf78-b283-4db3-ecf8-ad7b39bef8b9" colab={"base_uri": "https://localhost:8080/", "height": 35}
print('Reading train dataset (Train 60000.cdb)...')
X_train, Y_train = read_hoda_dataset(dataset_path='./HodaDatasetReader/DigitDB/Train 60000.cdb',
                                images_height=32,
                                images_width=32,
                                one_hot=False,
                                reshape=False)

# + id="sVgt0jwK_uxH" colab_type="code" outputId="23cadd02-237c-4892-b368-0d0ebe37aabc" colab={"base_uri": "https://localhost:8080/", "height": 116}
from keras.utils.np_utils import to_categorical
Y_train = to_categorical(Y_train)
print (X_train[0].shape)

# + id="DEtoFtfx_-Ky" colab_type="code" colab={}
import keras 
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense

# + id="DGqNBZ1TC1QP" colab_type="code" outputId="2b3f58d9-f670-4cf5-ad4d-b2d265fa6ca0" colab={"base_uri": "https://localhost:8080/", "height": 176}
model = Sequential()
#Layer 1
#Conv Layer 1
model.add(Conv2D(filters = 6, 
                 kernel_size = 5, 
                 strides = 1, 
                 activation = 'relu', 
                 input_shape = (32,32,1)))
#Pooling layer 1
model.add(MaxPooling2D(pool_size = 2, strides = 2))
#Layer 2
#Conv Layer 2
model.add(Conv2D(filters = 16, 
                 kernel_size = 5,
                 strides = 1,
                 activation = 'relu',
                 input_shape = (14,14,6)))
#Pooling Layer 2
model.add(MaxPooling2D(pool_size = 2, strides = 2))

# + id="0An43wYjDIL7" colab_type="code" colab={}
#Flatten
model.add(Flatten())
#Layer 3
#Fully connected layer 1
model.add(Dense(units = 120, activation = 'relu'))
#Layer 4
#Fully connected layer 2
model.add(Dense(units = 84, activation = 'relu'))
#Layer 5
#Output Layer
model.add(Dense(units = 10, activation = 'softmax'))

# + id="I2gVmCOoDNoO" colab_type="code" outputId="3c64204d-3dec-46a4-efe0-bb77a301e785" colab={"base_uri": "https://localhost:8080/", "height": 1000}
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])

model.fit(X_train ,Y_train, steps_per_epoch = 10, epochs = 42)

# + id="wVOj1V1NEynC" colab_type="code" outputId="81bcbdf4-ac2a-40f3-f3a5-36d2efb6db2b" colab={"base_uri": "https://localhost:8080/", "height": 35}
print('Reading test dataset (Test 20000.cdb)...')
X_test, Y_test = read_hoda_dataset(dataset_path='./HodaDatasetReader/DigitDB/Test 20000.cdb',
                              images_height=32,
                              images_width=32,
                              one_hot=False,
                              reshape=False)

# + id="0qGANMNKFlKG" colab_type="code" outputId="4d3c2c1d-212b-464d-94fa-9a87714b7a26" colab={"base_uri": "https://localhost:8080/", "height": 52}
y_pred = model.predict(X_test)

#Converting one hot vectors to labels
labels = np.argmax(y_pred, axis = 1)
acc=0;

#Accuracy on test
x=[]
false_values=[]
for i in range(len(labels)):
  if labels[i]==Y_test[i]:
    acc+=1
  else:
    x=y_pred[i]
    prob_of_true=x[int(Y_test[i])]
    false_values.append((i,prob_of_true))
   
from operator import itemgetter
very_bad= sorted(false_values,key=itemgetter(1))
very_bad=very_bad[0:9]

print("ACCURACY on test =")
print(acc/len(labels))

out_img=[]
for pic in very_bad:
  out_img.append(X_test[int(pic[0])])

# + id="KghBb2uTNT75" colab_type="code" outputId="962539fd-69ea-4a1c-b0c4-f482d0e838dd" colab={"base_uri": "https://localhost:8080/", "height": 1000}
#Show 10 worst images

import matplotlib.pyplot as plt
for image in out_img:
  pixels = np.array(image, dtype='uint8')
  # Reshape the array into 28 x 28 array (2-dimensional array)
  pixels = pixels.reshape((32, 32))
  plt.imshow(pixels, cmap='gray')
  plt.show()

# + id="uIaSe_cNlwR0" colab_type="code" outputId="bd895a9a-195c-401c-ba9f-0efe9edabea6" colab={"base_uri": "https://localhost:8080/", "height": 191}
#confusion matrix
        from sklearn.metrics import confusion_matrix
        cm = confusion_matrix(Y_test, labels)

        print(cm)

StarcoderdataJupyterScriptsDedupFiltered