Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save anupam-io/5f235843cdc5f60897e52cb64ac298e7 to your computer and use it in GitHub Desktop.
Save anupam-io/5f235843cdc5f60897e52cb64ac298e7 to your computer and use it in GitHub Desktop.
Captcha-using-tensorflow-on-chars74K.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Captcha-using-tensorflow-on-chars74K.ipynb",
"provenance": [],
"collapsed_sections": [],
"include_colab_link": true
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/akcgjc007/5f235843cdc5f60897e52cb64ac298e7/captcha-using-tensorflow-on-chars74k.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"metadata": {
"id": "eBaKsoPeoykt"
},
"source": [
"# Developer docs\n",
"# https://www.tensorflow.org/tutorials/keras/classification\n",
"# https://www.tensorflow.org/api_docs/python/tf/keras/Model\n",
"\n",
"# Importing modules\n",
"from os import listdir, system\n",
"import pandas as pd\n",
"import numpy as np\n",
"import sys\n",
"import time\n",
"import pandas as pd\n",
"\n",
"import cv2 as cv2\n",
"import imutils\n",
"import imageio\n",
"import pickle\n",
"from IPython.display import clear_output, display, HTML\n",
"\n",
"\n",
"from matplotlib.pyplot import imshow\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.patches as patches\n",
"\n",
"import tensorflow as tf\n",
"from skimage.io import imread\n",
"from skimage import measure\n",
"from skimage.measure import regionprops\n",
"from skimage.transform import resize\n",
"from scipy import ndimage\n",
"\n",
"\n",
"# Downloading dataset from: http://www.ee.surrey.ac.uk/CVSSP/demos/chars74k/\n",
"# You can comment this after running once.\n",
"\n",
"# !wget http://www.ee.surrey.ac.uk/CVSSP/demos/chars74k/EnglishImg.tgz\n",
"# !tar -xvzf ./EnglishImg.tgz && rm -rf EnglishImg.tgz\n",
"# !git clone https://github.com/captcha-breakers/dataset-simple-uppercase.git"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "9IIDQCtPpAxe",
"outputId": "b4e51a26-7c79-43d9-f497-7c018c14171b"
},
"source": [
"# Setting up folders\n",
"base_dir = \"./English/Img/GoodImg/Bmp/\"\n",
"folders = listdir(base_dir)\n",
"folders.sort()\n",
"folders = folders[10:36]\n",
"print(folders)\n",
"\n",
"# Setting image_size\n",
"image_size = (24, 24)"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"['Sample011', 'Sample012', 'Sample013', 'Sample014', 'Sample015', 'Sample016', 'Sample017', 'Sample018', 'Sample019', 'Sample020', 'Sample021', 'Sample022', 'Sample023', 'Sample024', 'Sample025', 'Sample026', 'Sample027', 'Sample028', 'Sample029', 'Sample030', 'Sample031', 'Sample032', 'Sample033', 'Sample034', 'Sample035', 'Sample036']\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "dnjsqbFbpFOQ",
"outputId": "2b963fb0-ddc0-40e4-d458-e612624080eb"
},
"source": [
"# Collecting images from folders\n",
"data = []\n",
"total_images = 0\n",
"for i in folders:\n",
" all_images = listdir(base_dir+i)[:150]\n",
" total_images+=len(all_images)\n",
" currentlabel = int(i[6:])-11\n",
"\n",
" for name in all_images:\n",
" image = cv2.imread(base_dir+i+\"/\"+name)\n",
" image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
" image = cv2.resize(image, image_size)\n",
"\n",
" # Filtering\n",
" image = cv2.GaussianBlur(image,(5,5),0)\n",
" image = cv2.Laplacian(image,cv2.CV_64F)\n",
" image = cv2.GaussianBlur(image,(5,5),0)\n",
" kernel = np.ones((3, 3), np.uint8)\n",
" image = cv2.filter2D(image,-1,kernel)\n",
" \n",
" data.append((image/255, currentlabel))\n",
" \n",
" print(\"Images for\", chr(65-11+int(i[6:])),\":\", len(all_images))\n",
"print(\"Total images: \", total_images)"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"Images for A : 150\n",
"Images for B : 115\n",
"Images for C : 150\n",
"Images for D : 150\n",
"Images for E : 150\n",
"Images for F : 79\n",
"Images for G : 143\n",
"Images for H : 150\n",
"Images for I : 150\n",
"Images for J : 77\n",
"Images for K : 92\n",
"Images for L : 150\n",
"Images for M : 149\n",
"Images for N : 150\n",
"Images for O : 150\n",
"Images for P : 150\n",
"Images for Q : 35\n",
"Images for R : 150\n",
"Images for S : 150\n",
"Images for T : 150\n",
"Images for U : 92\n",
"Images for V : 84\n",
"Images for W : 67\n",
"Images for X : 80\n",
"Images for Y : 67\n",
"Images for Z : 55\n",
"Total images: 3085\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "kBEvr3VRpKLz"
},
"source": [
"# Shuffling data & making train/test\n",
"np.random.shuffle(data)\n",
"n = len(data)\n",
"p = int(80*n/100)\n",
"\n",
"train_images = []\n",
"train_labels = []\n",
"test_images = []\n",
"test_labels = []\n",
"for i in range(n):\n",
" if i<p:\n",
" train_images.append(data[i][0])\n",
" train_labels.append(data[i][1])\n",
" else:\n",
" test_images.append(data[i][0])\n",
" test_labels.append(data[i][1])"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "yWtextPZLnvH",
"outputId": "b81a781c-7a29-4b66-9a04-308e0b7da307"
},
"source": [
"# Visualizing Training data\n",
"fig = plt.figure(figsize=(10, 7))\n",
"fig.add_subplot(1, 5, 1)\n",
"imshow(data[0][0])\n",
"fig.add_subplot(1, 5, 2)\n",
"imshow(data[1][0])\n",
"fig.add_subplot(1, 5, 3)\n",
"imshow(data[2][0])\n",
"fig.add_subplot(1, 5, 4)\n",
"imshow(data[3][0])\n",
"fig.add_subplot(1, 5, 5)\n",
"imshow(data[4][0])\n",
"plt.show()"
],
"execution_count": null,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x504 with 5 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "3Yws9xKLpNHf",
"outputId": "b2846e52-671f-4af1-c81a-2403996f574d"
},
"source": [
"# Making the model and training it\n",
"model = tf.keras.Sequential([\n",
" tf.keras.layers.Flatten(input_shape=image_size),\n",
" tf.keras.layers.Dense(200, activation='relu'),\n",
" tf.keras.layers.Dense(200, activation='relu'),\n",
" tf.keras.layers.Dense(200, activation='relu'),\n",
" tf.keras.layers.Dense(26)\n",
"])\n",
"model.compile(\n",
" optimizer='adam',\n",
" loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
" metrics=['accuracy']\n",
")\n",
"model.fit(np.array(train_images), np.array(train_labels), epochs=20, batch_size=100)"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"Epoch 1/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 3.1299 - accuracy: 0.1160\n",
"Epoch 2/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 2.3422 - accuracy: 0.3871\n",
"Epoch 3/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 1.5287 - accuracy: 0.6219\n",
"Epoch 4/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 1.0529 - accuracy: 0.7270\n",
"Epoch 5/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.7947 - accuracy: 0.7953\n",
"Epoch 6/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.5809 - accuracy: 0.8475\n",
"Epoch 7/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.4346 - accuracy: 0.8993\n",
"Epoch 8/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.3273 - accuracy: 0.9325\n",
"Epoch 9/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.2609 - accuracy: 0.9497\n",
"Epoch 10/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.2018 - accuracy: 0.9698\n",
"Epoch 11/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.1508 - accuracy: 0.9827\n",
"Epoch 12/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.1195 - accuracy: 0.9888\n",
"Epoch 13/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0864 - accuracy: 0.9917\n",
"Epoch 14/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0732 - accuracy: 0.9970\n",
"Epoch 15/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0590 - accuracy: 0.9971\n",
"Epoch 16/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0471 - accuracy: 0.9963\n",
"Epoch 17/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0357 - accuracy: 0.9987\n",
"Epoch 18/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0318 - accuracy: 0.9988\n",
"Epoch 19/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0226 - accuracy: 0.9999\n",
"Epoch 20/20\n",
"25/25 [==============================] - 0s 2ms/step - loss: 0.0228 - accuracy: 0.9996\n"
],
"name": "stdout"
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f6d30186670>"
]
},
"metadata": {
"tags": []
},
"execution_count": 36
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "db79yJnOzy7g",
"outputId": "598492f9-a611-4338-d5e8-33599d0e108b"
},
"source": [
"system(\"mkdir -p models\")\n",
"# model.save('./models/my_model_84.h5')\n",
"\n",
"# Predicting on test images\n",
"probability_model = tf.keras.Sequential([model, tf.keras.layers.Softmax()])\n",
"predictions = probability_model.predict(np.array(test_images))\n",
"\n",
"p=0\n",
"for i in range(len(test_images)):\n",
" if np.argmax(predictions[i]) == test_labels[i]:\n",
" p+=1\n",
"\n",
"print(\"Test accuracy: \", 100*p/len(test_images))"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"Test accuracy: 77.47163695299838\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "DcEypgpaawjr",
"outputId": "457c04b4-7a61-4861-cd01-1f62691c302f"
},
"source": [
"fil_dir = \"./dataset-simple-uppercase/data/\"\n",
"total = 0\n",
"char_p = 0\n",
"char_n = 0\n",
"cap_p = 0\n",
"cap_n = 0\n",
"\n",
"for fil in listdir(fil_dir):\n",
" image = cv2.imread(fil_dir + fil)\n",
"\n",
" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) \n",
" _, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)\n",
"\n",
" cnts, new = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n",
" cnts=sorted(cnts, key = cv2.contourArea, reverse = True)[:30] \n",
" captcha = np.invert(thresh)\n",
" labelled_captcha = measure.label(captcha)\n",
"\n",
" character_dimensions = (0.20*captcha.shape[0], 0.8*captcha.shape[0], 0.03*captcha.shape[1], 0.25*captcha.shape[1])\n",
" min_height, max_height, min_width, max_width = character_dimensions\n",
"\n",
" characters = []\n",
" charactersx = []\n",
" counter=0\n",
" column_list = []\n",
" row_list = []\n",
" d = []\n",
" for regions in regionprops(labelled_captcha):\n",
" y0, x0, y1, x1 = regions.bbox\n",
" region_height = y1 - y0\n",
" region_width = x1 - x0\n",
"\n",
" if region_height > min_height and region_height < max_height and region_width > min_width and region_width < max_width:\n",
" roi = captcha[y0:y1, x0:x1]\n",
" resized_char = cv2.copyMakeBorder(roi, 10, 10, 10, 10, cv2.BORDER_CONSTANT)\n",
" resized_char = resize(roi, image_size)\n",
" column_list.append(x0)\n",
" d.append([x0, resized_char])\n",
" \n",
" sorted(d)\n",
" predicted_captcha = \"\" \n",
" ind=0\n",
" for _,each_character in d:\n",
" x_p,y_p = 3,3\n",
" each_character = cv2.copyMakeBorder(each_character, x_p, x_p, y_p, y_p, cv2.BORDER_CONSTANT)\n",
" each_character = cv2.resize(each_character,image_size)\n",
"# imshow(each_character), plt.show()\n",
" each_character = each_character.reshape(1, -1)\n",
" \n",
" result, = probability_model.predict([each_character])\n",
" if fil[ind] == chr(65+np.argmax(result)):char_p += 1\n",
" else:char_n += 1\n",
" \n",
" predicted_captcha+=chr(65+np.argmax(result))\n",
" ind += 1\n",
" total+=1\n",
" \n",
" \n",
" \n",
" if fil[:6] == predicted_captcha: cap_p+=1\n",
" else: cap_n+=1\n",
" \n",
" \"\"\"Statistics\"\"\"\n",
"\n",
" stats = pd.DataFrame(\n",
" [\n",
" [char_p, char_n, 100*char_p/(char_p+char_n)], \n",
" [cap_p, cap_n, 100*cap_p/(cap_p+cap_n)]\n",
" ], \n",
" index=[\"Char\", \"Captcha\"],\n",
" columns=[\"Pos\", \"Neg\", \"Acc\"]\n",
" )\n",
" \n",
" clear_output(wait=True)\n",
" display(HTML(stats.to_html()))\n",
" print(\"Current file: \", total)\n",
" print(fil[:6], \":\", predicted_captcha)"
],
"execution_count": null,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Pos</th>\n",
" <th>Neg</th>\n",
" <th>Acc</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>Char</th>\n",
" <td>43548</td>\n",
" <td>13407</td>\n",
" <td>76.460363</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Captcha</th>\n",
" <td>3152</td>\n",
" <td>6848</td>\n",
" <td>31.520000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "stream",
"text": [
"Current file: 10000\n",
"FXYQIR : PXYQR\n"
],
"name": "stdout"
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment