[
  {
    "path": ".dockerignore",
    "content": ".git\n.vscode\n.idea\nREADME.md\nLICENSE"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\nvenv\n__pycache__/"
  },
  {
    "path": ".travis.yml",
    "content": "dist: bionic\nsudo: required\nservices:\n  - docker\nlanguage: bash\n\nbranches:\n  only:\n  - master\n\nenv:\n  - IMAGE_NAME=12306_code_server DOCKER_CLI_EXPERIMENTAL=enabled\n\naddons:\n  apt:\n    packages:\n      - docker-ce\n\ninstall:\n  - docker --version\n  - docker run --rm --privileged multiarch/qemu-user-static:register --reset\n  - chmod +x qemu/qemu-aarch64-static && chmod +x qemu/qemu-arm-static\n\nscript:\n  - |\n    # 构建docker镜像\n    IMAGE_ARCH=\"amd64 arm32v7 arm64v8\"\n    COMMIT_SHA=$(git log -1 --pretty=format:\"%H\")\n    LATEST_TAG_SHA=$(git rev-list --tags --max-count=1)\n\n    for THIS_ARCH in ${IMAGE_ARCH}; do\n      docker build -t $IMAGE_NAME:$THIS_ARCH \\\n      -f docker/$THIS_ARCH-Dockerfile .\n      if [ $COMMIT_SHA == $LATEST_TAG_SHA ]; then\n        docker tag $IMAGE_NAME:$THIS_ARCH $IMAGE_NAME:$THIS_ARCH-$(git describe --abbrev=0 --tags)\n      fi\n    done\n    \n    # 推送镜像到docker hub\n    if [ \"$TRAVIS_PULL_REQUEST\" = \"false\" ]; then\n      for THIS_ARCH in ${IMAGE_ARCH}; do\n        #  标记镜像\n        docker tag $IMAGE_NAME:$THIS_ARCH $DOCKER_USERNAME/$IMAGE_NAME:$THIS_ARCH\n        if [ $COMMIT_SHA == $LATEST_TAG_SHA ]; then\n          docker tag $DOCKER_USERNAME/$IMAGE_NAME:$THIS_ARCH $DOCKER_USERNAME/$IMAGE_NAME:$THIS_ARCH-$(git describe --abbrev=0 --tags)\n        fi\n      done\n      echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin\n      docker push $DOCKER_USERNAME/$IMAGE_NAME\n      docker manifest create $DOCKER_USERNAME/$IMAGE_NAME:latest $DOCKER_USERNAME/$IMAGE_NAME:amd64 $DOCKER_USERNAME/$IMAGE_NAME:arm32v7 $DOCKER_USERNAME/$IMAGE_NAME:arm64v8\n      docker manifest inspect $DOCKER_USERNAME/$IMAGE_NAME:latest\n      docker manifest push $DOCKER_USERNAME/$IMAGE_NAME:latest\n      if [ $COMMIT_SHA == $LATEST_TAG_SHA ]; then\n        TAG=$(git describe --abbrev=0 --tags)\n        docker manifest create $DOCKER_USERNAME/$IMAGE_NAME:$TAG $DOCKER_USERNAME/$IMAGE_NAME:amd64 $DOCKER_USERNAME/$IMAGE_NAME:arm32v7 $DOCKER_USERNAME/$IMAGE_NAME:arm64v8\n        docker manifest inspect $DOCKER_USERNAME/$IMAGE_NAME:$TAG\n        docker manifest push $DOCKER_USERNAME/$IMAGE_NAME:$TAG\n      fi\n    fi"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 尹傲雄\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# 12306验证码识别服务器\r\n\r\n[![Uptime Robot status](https://img.shields.io/uptimerobot/status/m783635180-ab3d4772f147c2a3b92f8fe5)](https://stats.uptimerobot.com/oyKyLhjJQ/783635180) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m783635180-ab3d4772f147c2a3b92f8fe5)](https://stats.uptimerobot.com/oyKyLhjJQ/783635180) [![Build Status](https://travis-ci.org/YinAoXiong/12306_code_server.svg?branch=master)](https://travis-ci.org/YinAoXiong/12306_code_server) [![Docker Pulls](https://img.shields.io/docker/pulls/yinaoxiong/12306_code_server)](https://hub.docker.com/r/yinaoxiong/12306_code_server)\r\n\r\n该项目用于构建自托管的12306验证码识别服务器，本项目的全部模型和部分代码来自于此项目 [easy12306](https://github.com/zhaipro/easy12306)，使用该项目构建的api符合 [12306购票小助手](https://github.com/testerSunshine/12306)云打码格式可以直接调用。\r\n\r\n提供一个部署好的线上版本, [https://12306.yinaoxiong.cn](https://12306.yinaoxiong.cn/),部署在腾讯云1核1G的学生机上不保证可用性,服务状态可以通过 [https://stats.uptimerobot.com/oyKyLhjJQ/783635180](https://stats.uptimerobot.com/oyKyLhjJQ/783635180)查看.\r\n\r\n\r\n\r\n## 接口规范\r\n\r\n### 请求\r\n\r\n- Method: **POST**\r\n- URL:  ```/verify/base64/```\r\n- Headers: Content-Type: application/x-www-form-urlencoded\r\n- Body: \r\n  imageFile=>Base64 encoding of the image\r\n\r\n### 响应\r\n\r\n- Headers：Content-Type:application/json\r\n- Body：\r\n\r\n```json\r\n{\r\n    \"code\": 0,\r\n    \"data\": [\r\n        \"1\",  //答案图片的编号数组\r\n        \"3\"\r\n    ],\r\n    \"massage\": \"识别成功\"\r\n}\r\n{\r\n    \"code\": 1,\r\n    \"data\": [\r\n    ],\r\n    \"massage\": \"识别失败\"\r\n}\r\n```\r\n\r\n\r\n\r\n## python版本支持\r\n\r\n- [x] 3.5-3.7\r\n\r\n## 平台支持\r\n\r\n- [x] amd64\r\n- [x] arm64v8\r\n- [x] arm32v7\r\n\r\n其中arm平台建议通过docker运行\r\n\r\n## 部署\r\n\r\n### docker部署(推荐)\r\n\r\n使用docker可以使用如下命令快速部署:\r\n\r\n\r\n  ```shell\r\n  docker run -d -p 8080:80 --name 12306 yinaoxiong/12306_code_server\r\n  ```\r\n\r\n### docker-compose部署(推荐)\r\n\r\n\r\n```yaml\r\nversion: \"3\"\r\n\r\nservices:\r\n  code_12306:\r\n    image: yinaoxiong/12306_code_server\r\n    ports:\r\n      - 5002:80 #可以根据需要修改端口\r\n    environment:\r\n      - WORKERS=1 #gunicorn works 默认为1可以根据服务器配置自行调整\r\n    restart: always\r\n  \r\n```\r\n\r\n### 通过源码部署\r\n\r\n1. 克隆并进入项目\r\n\r\n   ```shell\r\n   git clone https://github.com/YinAoXiong/12306_code_server.git\r\n   cd 12306_code_server\r\n   ```\r\n\r\n2. 安装依赖 自行根据平台和python选择对应的tflite（下面的例子为amd64，python3.7，其他情况对应的下载地址见 [https://www.tensorflow.org/lite/guide/python](https://www.tensorflow.org/lite/guide/python)，可自行在requirements.txt中替换）\r\n\r\n   ```shell\r\n   pip3 install -r requirements.txt\r\n   ```\r\n\r\n3. 下载模型文件\r\n\r\n    ```shell\r\n    bash download_model.sh\r\n    ```\r\n    从GitHub下载慢的话可以选择执行下面的命令\r\n\r\n    ```shell\r\n    wget -c https://cdn.yinaoxiong.cn/models/image.model.tflite\r\n    wget -c https://cdn.yinaoxiong.cn/models/text.model.tflite\r\n    ```\r\n\r\n4. 运行 默认workers为1，使用80端口，可以自行修改 gunicorn.conf\r\n\r\n   ```shell\r\n   gunicorn app:app -c gunicorn.conf.py\r\n   ```\r\n\r\n不推荐在arm平台上使用源码部署,依赖安装有些麻烦.\r\n\r\n## 致谢\r\n\r\n- [easy12306](https://github.com/zhaipro/easy12306) 提供项目运行的model\r\n-  [12306购票小助手](https://github.com/testerSunshine/12306)源于该项目的一个issue\r\n- ~~[tensorflow-on-arm](https://github.com/lhelontra/tensorflow-on-arm)提供arm上运行的tensorflow python包~~ v1.1版本后开始使用tflite而非keras\r\n"
  },
  {
    "path": "app.py",
    "content": "# -*- coding: utf-8 -*-\nimport io\nimport base64\nimport flask\nimport numpy as np\nfrom PIL import Image, ImageFile\nfrom verify import pretreatment\nimport tflite_runtime.interpreter as tflite\n\napp = flask.Flask(__name__)\n# 模型的全局变量\ntextModel = None\nimgModel = None\n# 设置加载截断的图片，解决issue #10\nImageFile.LOAD_TRUNCATED_IMAGES = True\n\n\n@app.before_first_request\ndef load_model():\n    '''\n    加载模型函数\n    :return:\n    '''\n    global textModel\n    global imgModel\n    textModel = tflite.Interpreter(\n        'text.model.tflite')\n    textModel.allocate_tensors()\n    imgModel = tflite.Interpreter(\n        'image.model.tflite')\n    imgModel.allocate_tensors()\n\n\ndef predict(model, input):\n    input_details = model.get_input_details()\n    output_details = model.get_output_details()\n    model.set_tensor(input_details[0]['index'], np.float32(input))\n    model.invoke()\n    result = model.get_tensor(output_details[0]['index'])\n    return result\n\n\ndef base64_to_image(base64_code):\n    '''\n    :param base64_code: base64编码的图片\n    :return: bgr格式的图片\n    '''\n    # base64解码\n    img_data = base64.b64decode(base64_code)\n    # 读取图片\n    img = np.asarray(Image.open(io.BytesIO(img_data)))\n    # 转换为bgr格式\n    img = img[..., ::-1]\n\n    return img\n\n\ndef get_text(img, offset=0):\n    '''\n    得到图片中文字的部分\n    :param img: 原始图像\n    :param offset:\n    :return: 文字部分的灰度图像\n    '''\n    text = pretreatment.get_text(img, offset)\n    text = text[..., 0] * 0.114 + text[..., 1] * 0.587 + text[\n        ..., 2] * 0.299\n    text = text / 255.0\n    h, w = text.shape\n    text.shape = (1, h, w, 1)\n    return text\n\n\ndef preprocess_input(x):\n    x = x.astype('float32')\n    # 我是用cv2来读取的图片，其已经是BGR格式了\n    mean = [103.939, 116.779, 123.68]\n    x -= mean\n    return x\n\n\n@app.route('/verify/base64/', methods=['POST'])\ndef predict_verify():\n    verify_titles = ['打字机', '调色板', '跑步机', '毛线', '老虎', '安全帽', '沙包', '盘子', '本子', '药片', '双面胶', '龙舟', '红酒', '拖把', '卷尺',\n                     '海苔', '红豆', '黑板', '热水袋', '烛台', '钟表', '路灯', '沙拉', '海报', '公交卡', '樱桃', '创可贴', '牌坊', '苍蝇拍', '高压锅',\n                     '电线', '网球拍', '海鸥', '风铃', '订书机', '冰箱', '话梅', '排风机', '锅铲', '绿豆', '航母', '电子秤', '红枣', '金字塔', '鞭炮',\n                     '菠萝', '开瓶器', '电饭煲', '仪表盘', '棉棒', '篮球', '狮子', '蚂蚁', '蜡烛', '茶盅', '印章', '茶几', '啤酒', '档案袋', '挂钟',\n                     '刺绣',\n                     '铃铛', '护腕', '手掌印', '锦旗', '文具盒', '辣椒酱', '耳塞', '中国结', '蜥蜴', '剪纸', '漏斗', '锣', '蒸笼', '珊瑚', '雨靴',\n                     '薯条',\n                     '蜜蜂', '日历', '口哨']\n    if flask.request.method == 'POST':\n        # 读取并预处理验证码\n        img = flask.request.form['imageFile']\n        img = base64_to_image(img)\n        text = get_text(img)\n        imgs = np.array(list(pretreatment._get_imgs(img)))\n        imgs = preprocess_input(imgs)\n        text_list = []\n        label = predict(textModel, text)\n        label = label.argmax()\n        text = verify_titles[label]\n        text_list.append(text)\n        # 获取下一个词\n        # 根据第一个词的长度来定位第二个词的位置\n        if len(text) == 1:\n            offset = 27\n        elif len(text) == 2:\n            offset = 47\n        else:\n            offset = 60\n        text = get_text(img, offset=offset)\n        if text.mean() < 0.95:\n            label = predict(textModel, text)\n            label = label.argmax()\n            text = verify_titles[label]\n            text_list.append(text)\n\n        print(f\"题目为{text_list}\")\n        labels = predict(imgModel, imgs)\n        labels = labels.argmax(axis=1)\n        results = []\n        for pos, label in enumerate(labels):\n            l = verify_titles[label]\n            print(pos + 1, l)\n            if l in text_list:\n                results.append(str(pos + 1))\n        if(len(results) != 0):\n            return {'code': 0, 'massage': '识别成功', 'data': results}\n        else:\n            return {'code': 1, 'massage': '识别失败', 'data': results}\n\n\n@app.route('/')\ndef hello_world():\n    return 'Hello World!'\n\n\nif __name__ == '__main__':\n    app.run()\n"
  },
  {
    "path": "arm32v7-requirements.txt",
    "content": "flask==1.1.1\nPillow>=6.2.2\ngunicorn==19.9.0\ngevent==1.4.0\nnumpy==1.17.4\nhttps://dl.google.com/coral/python/tflite_runtime-1.14.0-cp37-cp37m-linux_armv7l.whl"
  },
  {
    "path": "arm64v8-requirements.txt",
    "content": "flask==1.1.1\ngunicorn==19.9.0\nhttps://cdn.yinaoxiong.cn/wheels/numpy-1.17.4-cp37-cp37m-linux_aarch64.whl\nhttps://cdn.yinaoxiong.cn/wheels/Pillow-7.1.2-cp37-cp37m-linux_aarch64.whl\nhttps://cdn.yinaoxiong.cn/wheels/greenlet-0.4.15-cp37-cp37m-linux_aarch64.whl\nhttps://cdn.yinaoxiong.cn/wheels/gevent-1.4.0-cp37-cp37m-linux_aarch64.whl\nhttps://dl.google.com/coral/python/tflite_runtime-1.14.0-cp37-cp37m-linux_aarch64.whl"
  },
  {
    "path": "docker/amd64-Dockerfile",
    "content": "FROM python:3.7-slim-buster\n\nLABEL maintainer=\"Yin Aoxiong <yinaoxiong@gmail.com>\" \\\n      reference=\"https://github.com/yinaoxiong/12306_code_server\"\n\nWORKDIR /app\n\nRUN set -ex && \\\n            apt-get update && \\\n            apt-get install -y wget && \\\n            apt-get clean && rm -rf /var/lib/apt/lists/*\n\nCOPY requirements.txt ./\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY download_model.sh ./\nRUN bash download_model.sh\n\nCOPY . .\n\n# 服务运行在80端口\nEXPOSE 80\n\nCMD [\"gunicorn\", \"app:app\", \"-c\", \"gunicorn.conf.py\"]\n"
  },
  {
    "path": "docker/arm32v7-Dockerfile",
    "content": "FROM arm32v7/python:3.7-slim-buster\n\nLABEL maintainer=\"Yin Aoxiong <yinaoxiong@gmail.com>\" \\\n      reference=\"https://github.com/yinaoxiong/12306_code_server\"\n\nWORKDIR /app\n\nCOPY qemu/qemu-arm-static /usr/bin/qemu-arm-static\n\nRUN set -ex && \\\n            apt-get update && \\\n            apt-get install -y libwebpdemux2 libzstd1 libopenjp2-7 libjbig0 libtiff5 liblcms2-2 libwebp6 libwebpmux3  \\\n            libatlas3-base libgfortran5 wget && \\\n            apt-get clean && rm -rf /var/lib/apt/lists/*\n\nCOPY arm32v7-requirements.txt ./\nRUN pip install --no-cache-dir --extra-index-url=https://www.piwheels.org/simple -r arm32v7-requirements.txt\n\nCOPY download_model.sh ./\nRUN bash download_model.sh\n\nCOPY . .\n\n# 服务运行在80端口\nEXPOSE 80\n\nCMD [\"gunicorn\", \"app:app\", \"-c\", \"gunicorn.conf.py\"]\n"
  },
  {
    "path": "docker/arm64v8-Dockerfile",
    "content": "FROM arm64v8/python:3.7-slim-buster\n\nLABEL maintainer=\"Yin Aoxiong <yinaoxiong@gmail.com>\" \\\n      reference=\"https://github.com/yinaoxiong/12306_code_server\"\n\nWORKDIR /app\n\nCOPY qemu/qemu-aarch64-static /usr/bin/qemu-aarch64-static\n\nRUN set -ex && \\\n            apt-get update && \\\n            apt-get install -y libwebpdemux2 libzstd1 libopenjp2-7 libjbig0 libtiff5 liblcms2-2 libwebp6 libwebpmux3  \\\n            libopenblas-base libgfortran5 wget && \\\n            apt-get clean && rm -rf /var/lib/apt/lists/*\n\nCOPY arm64v8-requirements.txt ./\nRUN pip install --no-cache-dir -r arm64v8-requirements.txt\n\nCOPY download_model.sh ./\nRUN bash download_model.sh\n\nCOPY . .\n\n# 服务运行在80端口\nEXPOSE 80\n\nCMD [\"gunicorn\", \"app:app\", \"-c\", \"gunicorn.conf.py\"]\n"
  },
  {
    "path": "download_model.sh",
    "content": "wget -c https://github.com/YinAoXiong/12306_code_server/releases/download/v1.1/text.model.tflite\nwget -c https://github.com/YinAoXiong/12306_code_server/releases/download/v1.1/image.model.tflite\n"
  },
  {
    "path": "gunicorn.conf.py",
    "content": "import multiprocessing\nimport os\n\nbind = \"0.0.0.0:80\"\nworkers = os.getenv(\"WORKERS\",1)\nworker_class = \"gevent\""
  },
  {
    "path": "requirements.txt",
    "content": "flask==1.1.1\nPillow>=6.2.2\ngunicorn==19.9.0\ngevent==1.4.0\nnumpy==1.17.4\nhttps://dl.google.com/coral/python/tflite_runtime-1.14.0-cp37-cp37m-linux_x86_64.whl"
  },
  {
    "path": "verify/__init__.py",
    "content": ""
  },
  {
    "path": "verify/localVerifyCode.py",
    "content": "# coding: utf-8\nimport TickerConfig\n\nif TickerConfig.AUTO_CODE_TYPE == 2:\n    import base64\n    import os\n    import numpy as np\n    from keras import models, backend\n    import tensorflow as tf\n    from verify import pretreatment\n    from verify.mlearn_for_image import preprocess_input\n    from io import BytesIO\n    from PIL import Image\n\n    graph = tf.get_default_graph()\n\nPATH = lambda p: os.path.abspath(\n    os.path.join(os.path.dirname(__file__), p)\n)\n\nTEXT_MODEL = \"\"\nIMG_MODEL = \"\"\n\n\ndef get_text(img, offset=0):\n    text = pretreatment.get_text(img, offset)\n    text = text[..., 0] * 0.114 + text[..., 1] * 0.587 + text[\n        ..., 2] * 0.299  # text = cv2.cvtColor(text, cv2.COLOR_BGR2GRAY)\n    text = text / 255.0\n    h, w = text.shape\n    text.shape = (1, h, w, 1)\n    return text\n\n\ndef base64_to_image(base64_code):\n    # base64解码\n    img_data = base64.b64decode(base64_code)\n    # 读取图片\n    img = np.asarray(Image.open(BytesIO(img_data)))\n    # 转换为bgr格式\n    img = img[..., ::-1]\n\n    return img\n\n\nclass Verify:\n    def __init__(self):\n        self.textModel = \"\"\n        self.imgModel = \"\"\n        self.loadImgModel()\n        self.loadTextModel()\n\n    def loadTextModel(self):\n        if not self.textModel:\n            self.textModel = models.load_model(PATH('../model.v2.0.h5'))\n        else:\n            print(\"无需加载模型model.v2.0.h5\")\n\n    def loadImgModel(self):\n        if not self.imgModel:\n            self.imgModel = models.load_model(PATH('../12306.image.model.h5'))\n\n    def verify(self, fn):\n        verify_titles = ['打字机', '调色板', '跑步机', '毛线', '老虎', '安全帽', '沙包', '盘子', '本子', '药片', '双面胶', '龙舟', '红酒', '拖把', '卷尺',\n                         '海苔', '红豆', '黑板', '热水袋', '烛台', '钟表', '路灯', '沙拉', '海报', '公交卡', '樱桃', '创可贴', '牌坊', '苍蝇拍', '高压锅',\n                         '电线', '网球拍', '海鸥', '风铃', '订书机', '冰箱', '话梅', '排风机', '锅铲', '绿豆', '航母', '电子秤', '红枣', '金字塔', '鞭炮',\n                         '菠萝', '开瓶器', '电饭煲', '仪表盘', '棉棒', '篮球', '狮子', '蚂蚁', '蜡烛', '茶盅', '印章', '茶几', '啤酒', '档案袋', '挂钟',\n                         '刺绣',\n                         '铃铛', '护腕', '手掌印', '锦旗', '文具盒', '辣椒酱', '耳塞', '中国结', '蜥蜴', '剪纸', '漏斗', '锣', '蒸笼', '珊瑚', '雨靴',\n                         '薯条',\n                         '蜜蜂', '日历', '口哨']\n        # 读取并预处理验证码\n        img = base64_to_image(fn)\n        text = get_text(img)\n        imgs = np.array(list(pretreatment._get_imgs(img)))\n        imgs = preprocess_input(imgs)\n        text_list = []\n        # 识别文字\n        self.loadTextModel()\n        global graph\n        with graph.as_default():\n            label = self.textModel.predict(text)\n        label = label.argmax()\n        text = verify_titles[label]\n        text_list.append(text)\n        # 获取下一个词\n        # 根据第一个词的长度来定位第二个词的位置\n        if len(text) == 1:\n            offset = 27\n        elif len(text) == 2:\n            offset = 47\n        else:\n            offset = 60\n        text = get_text(img, offset=offset)\n        if text.mean() < 0.95:\n            with graph.as_default():\n                label = self.textModel.predict(text)\n            label = label.argmax()\n            text = verify_titles[label]\n            text_list.append(text)\n        print(\"题目为{}\".format(text_list))\n        # 加载图片分类器\n        self.loadImgModel()\n        with graph.as_default():\n            labels = self.imgModel.predict(imgs)\n        labels = labels.argmax(axis=1)\n        results = []\n        for pos, label in enumerate(labels):\n            l = verify_titles[label]\n            print(pos + 1, l)\n            if l in text_list:\n                results.append(str(pos + 1))\n        return results\n\n\nif __name__ == '__main__':\n    pass\n    # verify(\"verify-img1.jpeg\")\n"
  },
  {
    "path": "verify/mlearn_for_image.py",
    "content": "# coding: utf-8\n\n\ndef preprocess_input(x):\n    x = x.astype('float32')\n    # 我是用cv2来读取的图片，其已经是BGR格式了\n    mean = [103.939, 116.779, 123.68]\n    x -= mean\n    return x\n"
  },
  {
    "path": "verify/pretreatment.py",
    "content": "#! env python\n# coding: utf-8\n# 功能：对图像进行预处理，将文字部分单独提取出来\n# 并存放到ocr目录下\n# 文件名为原验证码文件的文件名\n\n\ndef get_text(img, offset=0):\n    # 得到图像中的文本部分\n    return img[3:22, 120 + offset:177 + offset]\n\n\ndef _get_imgs(img):\n    interval = 5\n    length = 67\n    for x in range(40, img.shape[0] - length, interval + length):\n        for y in range(interval, img.shape[1] - length, interval + length):\n            yield img[x:x + length, y:y + length]\n"
  }
]