dataLoad.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. #!/usr/bin/env python3
  2. from PIL import Image
  3. from pprint import pprint
  4. import sys, time, os, requests, random, json, argparse
  5. import subprocess, yaml
  6. parser = argparse.ArgumentParser(description="S0urce.io utility program.")
  7. parser.add_argument("--download", help="Download Images", action="store_true")
  8. parser.add_argument("--train", help="Convert Images to Text", action="store_true")
  9. parser.add_argument(
  10. "JSON", type=str, nargs="?", help="Filename to save results", default="test.js"
  11. )
  12. args = parser.parse_args()
  13. # pprint(args)
  14. # Should we add the JSON in a file? (True is filename, False = do not do)
  15. # JSONME = 'test.js'
  16. JSONME = args.JSON
  17. # NOTE: To begin the insert of the JSONIFIED image and word its
  18. # // T
  19. # A JS comment with a uppercase T
  20. # To stop its
  21. # // t
  22. # A JS comment with a lowercase t
  23. # httpbin.org/headers
  24. sess = requests.Session()
  25. head = {
  26. "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"
  27. }
  28. sess.headers.update(head)
  29. ON = "X" # Dark pixel in an image
  30. OFF = "." # Light pixel in an image
  31. DIR = (
  32. "data"
  33. ) # Data directory name, do we really need this? Is it really going to change?
  34. INTENSITY = (
  35. 75
  36. ) # How bright does something have to be to trigger it being a dark or light pixel?
  37. # Looks like around 75 removes the extra stuff that s0urce.io does to prevent it from being just matching images.
  38. GREEN_DIFF = 10
  39. # How much brighter the green channel must be (compared to the others),
  40. # to be called green.
  41. # Check the environment, do we have all that we need?
  42. if not os.path.exists("images"):
  43. os.mkdir("images")
  44. if not os.path.exists("data"):
  45. os.mkdir("data")
  46. if not os.path.exists("words.yml"):
  47. with open('words.yml', 'w') as f: # Create a empty yaml file
  48. f.write('')
  49. def download(howhard, index):
  50. global sess
  51. """
  52. Download an image based upon how hard it is.
  53. On success, it saves the image file.
  54. Failure raises ConnectionError.
  55. """
  56. r = sess.get(f"http://s0urce.io/client/img/word/{howhard}/{index}")
  57. if r.status_code == 200:
  58. with open(f"images/{howhard}_{index}.png", "wb") as f:
  59. f.write(r.content)
  60. else:
  61. # We did not get a 200 Okay, log this... Hmm maybe we need to make a log file?
  62. # print( f'{howhard}_{index}.png ' + str(r.status_code) )
  63. raise ConnectionError(
  64. "http://s0urce.io/client/img/word/{0}/{1} returned status_code {2}".format(
  65. howhard, index, r.status_code
  66. )
  67. )
  68. def img_point(pix, x, y):
  69. """
  70. img_point, returns a pixel of an image,
  71. given the x and y on the image.
  72. """
  73. return pix[x, y]
  74. def img_avg(pix, x, y):
  75. """
  76. img_avg, returns the average brightness 0-255,
  77. given pixel, and the x and y on the image calls img_point,
  78. to get the individual rgb values to calculate,
  79. brightness. (Grey scale)
  80. """
  81. rgb = img_point(pix, x, y)
  82. # if(im.mode == 'P'):
  83. # rgb = pal[rgb*3:(rgb+1)*3]
  84. # if(im.mode == 'I'):
  85. # return rgb >> 8
  86. return int((rgb[0] + rgb[1] + rgb[2]) / 3)
  87. def is_set(pix, x, y):
  88. global INTENSITY
  89. """
  90. is_set, returns True or False of calculating,
  91. the brightness of the given point on a image,
  92. compared to given intensity.
  93. True means the brightness at the given x and y,
  94. is Less Than which means its dark.
  95. False means the brightness at the given x and y,
  96. is Greater Than which means its bright. (Grey Scale)
  97. """
  98. avg = img_avg(pix, x, y)
  99. return avg < INTENSITY
  100. def is_green(pix, x, y):
  101. """
  102. Is this pixel Green?
  103. """
  104. (red, green, blue, _) = img_point(pix, x, y)
  105. # Find the difference between green and the other values.
  106. other = red
  107. if blue > other:
  108. other = blue
  109. diff = green - other
  110. return diff > GREEN_DIFF
  111. def scan_img(pix, size):
  112. """
  113. scan_img, looks at a image and looks for dark pixels,
  114. if it is a dark pixel record the number and resize the,
  115. returned values to show where the most dark pixels on the,
  116. image are located. (Grey Scale)
  117. given pixel, and image size.
  118. returns start x, y and end x, y and total number of dark pixels.
  119. """
  120. total = 0
  121. sx = size[0]
  122. ex = 0
  123. sy = size[1]
  124. ey = 0
  125. for y in range(0, size[1]):
  126. for x in range(0, size[0]):
  127. pnt_is = is_set(pix, x, y)
  128. if pnt_is:
  129. total += 1
  130. if x < sx:
  131. sx = x
  132. if x > ex:
  133. ex = x
  134. if y < sy:
  135. sy = y
  136. if y > ey:
  137. ey = y
  138. # print (sx,ex,sy,ey)
  139. # give us a little border to work with
  140. if sx > 0:
  141. sx -= 1
  142. if ex < size[0]:
  143. ex += 1
  144. if sy > 0:
  145. sy -= 1
  146. if ey < size[1]:
  147. ey += 1
  148. # print (sx,ex,sy,ey)
  149. return (sx, sy, ex, ey, total)
  150. def output_image(pix, size):
  151. """
  152. For the size of the area we have reduced down to where the majority of dark pixels,
  153. are located, store all that into a list and return the list.
  154. given pixel for function passing.
  155. returns multiple strings in a list that are edited to use characters to represent,
  156. the dark and light pixels of the image. (Grey Scale)
  157. """
  158. result = []
  159. ex = size[0]
  160. sx = 0
  161. ey = size[1]
  162. sy = 0
  163. for y in range(sy, ey):
  164. s = ""
  165. for x in range(sx, ex):
  166. # if is_set(pix, x, y):
  167. if not is_green(pix, x, y):
  168. s += ON
  169. else:
  170. s += OFF
  171. result.append(s)
  172. return result
  173. def run(difficult, index):
  174. """
  175. run, represents a single execution of components to the image, (Actuall we do it 1 category at a time instead of just 1 single execution )
  176. those components do the following... (Each category has around 70 items so we standardize on 70, but )
  177. (not all of the categories have 70 and thus we print a File does not exist)
  178. We open and load the image, and get it's size,
  179. then we scan_img for dark and light pixels, <-- This narrows the image down to just the majority of dark pixels
  180. then from that we output the image line by line onto the screen after it has been output_image d into list form,
  181. Where we ask the user what the word is, and after that we save all that to a file in the data directory.
  182. """
  183. fname = f"images/{difficult}_{x}.png"
  184. if not os.path.exists(fname):
  185. print("Could not find '{0}'".format(fname))
  186. return False # We did not complete
  187. print(f"Loading: {fname}")
  188. im = Image.open(fname)
  189. pix = im.load()
  190. size = im.size
  191. print(f"Size: {size[0]} x {size[1]}")
  192. pal = im.getpalette()
  193. sx = 0
  194. ex = size[0]
  195. sy = 0
  196. ey = size[1]
  197. total = 0
  198. sx, sy, ex, ey, total = scan_img(pix, size)
  199. print(f"Chars within ({sx}, {sy}) - ({ex}, {ey}) total {total} pixels")
  200. img_s = output_image(pix, size)
  201. for l in img_s:
  202. print(l)
  203. word = input("Word: ")
  204. # Returns word so it can be stored in dictonary
  205. return word
  206. #print(f"Image saved to '{DIR}/{difficult}_{x}.txt' in byte string")
  207. # os.remove(f'{fname}') # Grr No bad bean, keep file for error checking
  208. # print(f"File '{fname}' automatically removed")
  209. def autotrain(difficult, index):
  210. """
  211. run, represents a single execution of components to the image, (Actuall we do it 1 category at a time instead of just 1 single execution )
  212. those components do the following... (Each category has around 70 items so we standardize on 70, but )
  213. (not all of the categories have 70 and thus we print a File does not exist)
  214. We open and load the image, and get it's size,
  215. then we scan_img for dark and light pixels, <-- This narrows the image down to just the majority of dark pixels
  216. then from that we output the image line by line onto the screen after it has been output_image d into list form,
  217. Where we ask the user what the word is, and after that we save all that to a file in the data directory.
  218. """
  219. # Re aranged the code so I can have it return after each word
  220. fname = f"images/{difficult}_{x}.png"
  221. if not os.path.exists(fname):
  222. print("Could not find '{0}'".format(fname))
  223. return False # We did not complete
  224. print(f"Loading: {fname}")
  225. fileout = "data/{0}_{1}".format(difficult, x)
  226. output = subprocess.run(
  227. ["tesseract", fname, fileout],
  228. stderr=subprocess.DEVNULL,
  229. # capture_output=False,
  230. shell=False,
  231. )
  232. with open(fileout + ".txt", "r") as fp:
  233. word = fp.read().strip()
  234. print(word)
  235. return word # Save this to the dict
  236. # Now to call all the previous functions
  237. if args.download:
  238. print("Downloading s0urce.io Words")
  239. print("EASY")
  240. # time.sleep(5)
  241. for e in range(0, 62):
  242. download("e", e)
  243. # time.sleep(random.randint(10, 15))
  244. print("MEDIUM")
  245. # time.sleep(5)
  246. for m in range(0, 66):
  247. download("m", m)
  248. # time.sleep(random.randint(10, 15))
  249. print("HARD")
  250. # time.sleep(5)
  251. for h in range(0, 55):
  252. download("h", h)
  253. # time.sleep(random.randint(10, 15))
  254. if args.train:
  255. # Img Processing: Run thru every single category and every single word
  256. wordDict = {}
  257. for level in ["e", "m", "h"]:
  258. for x in range(0, 66):
  259. at = autotrain(level, x)
  260. if(at != False): # If it is complete store it
  261. wordDict["{0}_{1}".format(level, x)] = at
  262. with open('words.yml', 'w') as f:
  263. yaml.dump(wordDict, f) # Writes it automatically into the file
  264. # ----------------------------------------------------------------------------------------
  265. # All below was in a seperate dataJS.py file... but now I have fixed it so it's 1 script!
  266. # Do we really need to worry about all this right now? (I think we have enough bugs to begin with.)
  267. JSONME = "false" # Do not execute
  268. if JSONME.lower() != "false":
  269. print("Now exporting to JSON")
  270. print(f"Targeting file: '{JSONME}'")
  271. time.sleep(5)
  272. def test(t):
  273. global DIR
  274. """
  275. given the filename, we read it and add it to a list and return the list.
  276. """
  277. fname = f"{DIR}/{t}.txt"
  278. r = []
  279. try:
  280. with open(fname, "r") as f:
  281. for l in f:
  282. r.append(l.strip())
  283. return r
  284. except FileNotFoundError:
  285. return None
  286. def insertJS(item):
  287. global JSON
  288. """
  289. Edits the file given and adds the JSONIFIED item to the file between 2 indicators,
  290. // T
  291. and
  292. // t
  293. In between the T and t will be replaced with the item.
  294. """
  295. item = json.dumps(item)
  296. item = f"{item},"
  297. r = []
  298. try:
  299. with open(f"{JSONME}", "r") as f:
  300. for l in f:
  301. if l != "":
  302. r.append(l.strip("\n"))
  303. else:
  304. r.append("")
  305. except FileNotFoundError:
  306. print(f"File {JSONME} Not Found!")
  307. sys.exit()
  308. c = 0
  309. for e in r:
  310. if "// T" == e:
  311. temp = r[c + 1]
  312. del r[c + 1]
  313. r.insert(c + 1, item)
  314. r.insert(c + 2, temp)
  315. elif "// t" == e:
  316. break
  317. c += 1
  318. with open(f"{JSONME}", "w") as f:
  319. for e in r:
  320. f.write(f"{e}\n")
  321. for x in range(0, 183):
  322. te = test(x)
  323. if te != None:
  324. word = te
  325. insertJS(word)
  326. # Regardless what we did let the user know we at least ran and we are now done
  327. print("Complete")