MrAR commited on
Commit
5f1ffb3
·
verified ·
1 Parent(s): 0a3a965

Upload 6 files

Browse files
example_captcha1.png.png ADDED
example_captcha2.png.png ADDED
example_captcha3.png.png ADDED
extract_single_letters_from_captchas.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import os.path
3
+ import cv2
4
+ import glob
5
+ import imutils
6
+
7
+
8
+ CAPTCHA_IMAGE_FOLDER = "generated_captcha_images"
9
+ OUTPUT_FOLDER = "extracted_letter_images"
10
+
11
+
12
+ # Get a list of all the captcha images we need to process
13
+ captcha_image_files = glob.glob(os.path.join(CAPTCHA_IMAGE_FOLDER, "*"))
14
+ counts = {}
15
+
16
+ # loop over the image paths
17
+ for (i, captcha_image_file) in enumerate(captcha_image_files):
18
+ print("[INFO] processing image {}/{}".format(i + 1, len(captcha_image_files)))
19
+
20
+ # Since the filename contains the captcha text (i.e. "2A2X.png" has the text "2A2X"),
21
+ # grab the base filename as the text
22
+ filename = os.path.basename(captcha_image_file)
23
+ captcha_correct_text = os.path.splitext(filename)[0]
24
+
25
+ # Load the image and convert it to grayscale
26
+ image = cv2.imread(captcha_image_file)
27
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
28
+
29
+ # Add some extra padding around the image
30
+ gray = cv2.copyMakeBorder(gray, 8, 8, 8, 8, cv2.BORDER_REPLICATE)
31
+
32
+ # threshold the image (convert it to pure black and white)
33
+ thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
34
+
35
+ # find the contours (continuous blobs of pixels) the image
36
+ contours = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
37
+
38
+ # Hack for compatibility with different OpenCV versions
39
+ contours = contours[1] if imutils.is_cv3() else contours[0]
40
+
41
+ letter_image_regions = []
42
+
43
+ # Now we can loop through each of the four contours and extract the letter
44
+ # inside of each one
45
+ for contour in contours:
46
+ # Get the rectangle that contains the contour
47
+ (x, y, w, h) = cv2.boundingRect(contour)
48
+
49
+ # Compare the width and height of the contour to detect letters that
50
+ # are conjoined into one chunk
51
+ if w / h > 1.25:
52
+ # This contour is too wide to be a single letter!
53
+ # Split it in half into two letter regions!
54
+ half_width = int(w / 2)
55
+ letter_image_regions.append((x, y, half_width, h))
56
+ letter_image_regions.append((x + half_width, y, half_width, h))
57
+ else:
58
+ # This is a normal letter by itself
59
+ letter_image_regions.append((x, y, w, h))
60
+
61
+ # If we found more or less than 4 letters in the captcha, our letter extraction
62
+ # didn't work correcly. Skip the image instead of saving bad training data!
63
+ if len(letter_image_regions) != 4:
64
+ continue
65
+
66
+ # Sort the detected letter images based on the x coordinate to make sure
67
+ # we are processing them from left-to-right so we match the right image
68
+ # with the right letter
69
+ letter_image_regions = sorted(letter_image_regions, key=lambda x: x[0])
70
+
71
+ # Save out each letter as a single image
72
+ for letter_bounding_box, letter_text in zip(letter_image_regions, captcha_correct_text):
73
+ # Grab the coordinates of the letter in the image
74
+ x, y, w, h = letter_bounding_box
75
+
76
+ # Extract the letter from the original image with a 2-pixel margin around the edge
77
+ letter_image = gray[y - 2:y + h + 2, x - 2:x + w + 2]
78
+
79
+ # Get the folder to save the image in
80
+ save_path = os.path.join(OUTPUT_FOLDER, letter_text)
81
+
82
+ # if the output directory does not exist, create it
83
+ if not os.path.exists(save_path):
84
+ os.makedirs(save_path)
85
+
86
+ # write the letter image to a file
87
+ count = counts.get(letter_text, 1)
88
+ p = os.path.join(save_path, "{}.png".format(str(count).zfill(6)))
89
+ cv2.imwrite(p, letter_image)
90
+
91
+ # increment the count for the current key
92
+ counts[letter_text] = count + 1
helpers.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import imutils
2
+ import cv2
3
+
4
+
5
+ def resize_to_fit(image, width, height):
6
+ """
7
+ A helper function to resize an image to fit within a given size
8
+ :param image: image to resize
9
+ :param width: desired width in pixels
10
+ :param height: desired height in pixels
11
+ :return: the resized image
12
+ """
13
+
14
+ # grab the dimensions of the image, then initialize
15
+ # the padding values
16
+ (h, w) = image.shape[:2]
17
+
18
+ # if the width is greater than the height then resize along
19
+ # the width
20
+ if w > h:
21
+ image = imutils.resize(image, width=width)
22
+
23
+ # otherwise, the height is greater than the width so resize
24
+ # along the height
25
+ else:
26
+ image = imutils.resize(image, height=height)
27
+
28
+ # determine the padding values for the width and height to
29
+ # obtain the target dimensions
30
+ padW = int((width - image.shape[1]) / 2.0)
31
+ padH = int((height - image.shape[0]) / 2.0)
32
+
33
+ # pad the image then apply one more resizing to handle any
34
+ # rounding issues
35
+ image = cv2.copyMakeBorder(image, padH, padH, padW, padW,
36
+ cv2.BORDER_REPLICATE)
37
+ image = cv2.resize(image, (width, height))
38
+
39
+ # return the pre-processed image
40
+ return image
solve_captchas_with_model.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from keras.models import load_model
2
+ from helpers import resize_to_fit
3
+ from imutils import paths
4
+ import numpy as np
5
+ import imutils
6
+ import cv2
7
+ import pickle
8
+
9
+
10
+ MODEL_FILENAME = "captcha_model.hdf5"
11
+ MODEL_LABELS_FILENAME = "model_labels.dat"
12
+ CAPTCHA_IMAGE_FOLDER = "generated_captcha_images"
13
+
14
+
15
+ # Load up the model labels (so we can translate model predictions to actual letters)
16
+ with open(MODEL_LABELS_FILENAME, "rb") as f:
17
+ lb = pickle.load(f)
18
+
19
+ # Load the trained neural network
20
+ model = load_model(MODEL_FILENAME)
21
+
22
+ # Grab some random CAPTCHA images to test against.
23
+ # In the real world, you'd replace this section with code to grab a real
24
+ # CAPTCHA image from a live website.
25
+ captcha_image_files = list(paths.list_images(CAPTCHA_IMAGE_FOLDER))
26
+ captcha_image_files = np.random.choice(captcha_image_files, size=(10,), replace=False)
27
+
28
+ # loop over the image paths
29
+ for image_file in captcha_image_files:
30
+ # Load the image and convert it to grayscale
31
+ image = cv2.imread(image_file)
32
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
33
+
34
+ # Add some extra padding around the image
35
+ image = cv2.copyMakeBorder(image, 20, 20, 20, 20, cv2.BORDER_REPLICATE)
36
+
37
+ # threshold the image (convert it to pure black and white)
38
+ thresh = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
39
+
40
+ # find the contours (continuous blobs of pixels) the image
41
+ contours = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
42
+
43
+ # Hack for compatibility with different OpenCV versions
44
+ contours = contours[1] if imutils.is_cv3() else contours[0]
45
+
46
+ letter_image_regions = []
47
+
48
+ # Now we can loop through each of the four contours and extract the letter
49
+ # inside of each one
50
+ for contour in contours:
51
+ # Get the rectangle that contains the contour
52
+ (x, y, w, h) = cv2.boundingRect(contour)
53
+
54
+ # Compare the width and height of the contour to detect letters that
55
+ # are conjoined into one chunk
56
+ if w / h > 1.25:
57
+ # This contour is too wide to be a single letter!
58
+ # Split it in half into two letter regions!
59
+ half_width = int(w / 2)
60
+ letter_image_regions.append((x, y, half_width, h))
61
+ letter_image_regions.append((x + half_width, y, half_width, h))
62
+ else:
63
+ # This is a normal letter by itself
64
+ letter_image_regions.append((x, y, w, h))
65
+
66
+ # If we found more or less than 4 letters in the captcha, our letter extraction
67
+ # didn't work correcly. Skip the image instead of saving bad training data!
68
+ if len(letter_image_regions) != 4:
69
+ continue
70
+
71
+ # Sort the detected letter images based on the x coordinate to make sure
72
+ # we are processing them from left-to-right so we match the right image
73
+ # with the right letter
74
+ letter_image_regions = sorted(letter_image_regions, key=lambda x: x[0])
75
+
76
+ # Create an output image and a list to hold our predicted letters
77
+ output = cv2.merge([image] * 3)
78
+ predictions = []
79
+
80
+ # loop over the lektters
81
+ for letter_bounding_box in letter_image_regions:
82
+ # Grab the coordinates of the letter in the image
83
+ x, y, w, h = letter_bounding_box
84
+
85
+ # Extract the letter from the original image with a 2-pixel margin around the edge
86
+ letter_image = image[y - 2:y + h + 2, x - 2:x + w + 2]
87
+
88
+ # Re-size the letter image to 20x20 pixels to match training data
89
+ letter_image = resize_to_fit(letter_image, 20, 20)
90
+
91
+ # Turn the single image into a 4d list of images to make Keras happy
92
+ letter_image = np.expand_dims(letter_image, axis=2)
93
+ letter_image = np.expand_dims(letter_image, axis=0)
94
+
95
+ # Ask the neural network to make a prediction
96
+ prediction = model.predict(letter_image)
97
+
98
+ # Convert the one-hot-encoded prediction back to a normal letter
99
+ letter = lb.inverse_transform(prediction)[0]
100
+ predictions.append(letter)
101
+
102
+ # draw the prediction on the output image
103
+ cv2.rectangle(output, (x - 2, y - 2), (x + w + 4, y + h + 4), (0, 255, 0), 1)
104
+ cv2.putText(output, letter, (x - 5, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 255, 0), 2)
105
+
106
+ # Print the captcha's text
107
+ captcha_text = "".join(predictions)
108
+ print("CAPTCHA text is: {}".format(captcha_text))
109
+
110
+ # Show the annotated image
111
+ cv2.imshow("Output", output)
112
+ cv2.waitKey()