修复KeyError: CONTENT-LENGTH,架设AI上色服务PaintsChainer,在Python 3.7版本下的方法

sample - design, daily - 修复KeyError: CONTENT-LENGTH,架设AI上色服务PaintsChainer,在Python 3.7版本下的方法

PaintsChainer是另一款基于机器学习的上色服务。在成功架设style2paints(V3)之后,尝试架设PaintsChainer进行功能对比。

以下是PaintsChainer的源代码:

https://github.com/pfnet/PaintsChainer

PaintsChainer同样使用CUDA,和style2paints在假设上有类似之处。但是它上传图片使用的Post方式却存在问题,在Python 3.6下这个问题不会发生。但如果使用新版本Python(笔者使用的是3.7.4)会导致如下错误代码:

File "...\lib\cgi.py", line 220, in parse_multipart
headers['Content-Length'] = pdict['CONTENT-LENGTH']
KeyError: 'CONTENT-LENGTH'

这是由于在Python 3.7的cgi.py中,在pdict下将CONTENT-LENGTH设置了为POST时Header内的必需字段。目前关于CONTENT-LENGTH还没有官方的介绍文档,但是在函数的介绍中,有提到它和Header原本的content-length不同。

目前由本人找到的,临时的解决办法是修改POST方法,姑且将content-length赋值给CONTENT-LENGTH放在Header中进行欺骗。事实证明这样做可以成功运行PaintsChainer。具体方法如下:

在server.py中,找到函数parse_POST,加入下面这行:

pdict['CONTENT-LENGTH'] = int(self.headers['content-length'])

这样就可以解决对CONTENT-LENGTH字段的需要。然后,找到函数do_POST,有下面这行:

id_str = re.sub(r'\W+', '', id_str.decode())

这估计是历史遗留问题,作者写代码的时候还是2017年。在最新的Python版本中,str字段在编码(encode)之前是不能解码(decode)的。而经过测试,这个id_str并不需要在Python 3.7.4下进行额外的解码操作。因此改成下面的:

id_str = re.sub(r'\W+', '', id_str())

至此顺利运行(修改后完整的server.py文件在文章末尾)。


另外,该服务调用CUDA时,使用的是CuPy而不是NumPy。而CuPy默认的缓存路径是用户文件夹路径,也就是:

C:\Users\用户名\AppData

而CuPy在读取时,默认仅支持ASCII码。这意味着,当你的用户名文件夹包含非英文字符的时候,CuPy读取缓存会报错。

至于你要修改指定编码为utf-8,还是干脆新建一个英文名称的本地账户,就看自己的能力和需求了。


完整的修改后的server.py文件如下:

#!/usr/bin/env python

import http.server

import sys
import time
import re

import argparse

from cgi import parse_header, parse_multipart
from urllib.parse import parse_qs


# sys.path.append('./cgi-bin/wnet')
sys.path.append('./cgi-bin/paint_x2_unet')
import cgi_exe
sys.path.append('./cgi-bin/helpers')
from platformAdapter import OSHelper

class MyHandler(http.server.CGIHTTPRequestHandler):

    t = []

    def __init__(self, req, client_addr, server):
        OSHelper.detect_environment()
        http.server.CGIHTTPRequestHandler.__init__(
            self, req, client_addr, server)

    def parse_POST(self):
        ctype, pdict = parse_header(self.headers['content-type'])
        pdict['boundary'] = bytes(pdict['boundary'], "utf-8")
        # Added 1 debug line here
        pdict['CONTENT-LENGTH'] = int(self.headers['content-length'])
        if ctype == 'multipart/form-data':
            postvars = parse_multipart(self.rfile, pdict)
        elif ctype == 'application/x-www-form-urlencoded':
            length = int(self.headers['content-length'])
            postvars = parse_qs(
                self.rfile.read(length),
                keep_blank_values=1)
        else:
            postvars = {}
        return postvars

    def log_t(self):
        if( args.debug ):
            self.t.append(time.time())
        return

    def print_log(self):
        if( args.debug ):
            for i, j in zip(self.t, self.t[1:]):
                print("time [sec]", j - i)
                self.t = []
        return



    def do_POST(self):
        self.log_t()
        form = self.parse_POST()
        self.log_t()

        if "id" in form:
            id_str = form["id"][0]
			# Changed 1 line here
			# original line:
			# id_str = re.sub(r'\W+', '', id_str.decode())
            id_str = re.sub(r'\W+', '', id_str())
        else:
            self.ret_result(False)
            return

        if( re.search('/post/*', self.path) != None ):
            self.post_process( form, id_str )
        elif ( re.search('/paint/*', self.path) != None ):
            self.paint_process( form, id_str )
        else:
            self.ret_result(False)
        return

    def post_process(self, form, id_str):

        if "line" in form:
            bin1 = form["line"][0]
            fout1 = open("./images/line/" + id_str + ".png", 'wb')
            fout1.write(bin1)
            fout1.close()
        else:
            self.ret_result(False)
            return

        if "ref" in form:
            bin2 = form["ref"][0]
            fout2 = open("./images/ref/" + id_str + ".png", 'wb')
            fout2.write(bin2)
            fout2.close()
        else:
            self.ret_result(False)
            return

        self.log_t()
        self.ret_result(True)
        self.log_t()
        self.print_log()

        return

    def paint_process(self, form, id_str):

        blur = 0
        if "blur" in form:
            blur = form["blur"][0].decode()
            try:
                blur = int(blur)
            except ValueError:
                blur = 0

        self.log_t()
        painter.colorize(id_str, form["step"][0].decode() if "step" in form else "C", blur=blur)

        self.log_t()
        self.ret_result(True)
        self.log_t()
        self.print_log()

        return



    def ret_result(self, success):
        if success:
            content = bytes(
                "{ 'message':'The command Completed Successfully' , 'Status':'200 OK','success':true , 'used':" + str(args.gpu) + "}", "UTF-8")
            self.send_response(200)
        else:
            content = bytes(
                "{ 'message':'The command Failed' , 'Status':'503 NG','success':false , 'used':" + str(args.gpu) + "}", "UTF-8")
            self.send_response(503)
        self.send_header("Content-type", "application/json")
        self.send_header("Content-Length", len(content))
        self.send_header("Access-Control-Allow-Origin", "*")  # hard coding...
        self.end_headers()
        self.wfile.write(content)
        self.log_t()

# set args 
if "__main__" in __name__:
    parser = argparse.ArgumentParser(
        description='chainer line drawing colorization server')
    parser.add_argument('--gpu', '-g', type=int, default=0,
                        help='GPU ID (negative value indicates CPU)')
    parser.add_argument('--mode', '-m', default="stand_alone",
                        help='set process mode')
    # other mode "post_server" "paint_server"

    parser.add_argument('--port', '-p', type=int, default=8000,
                        help='using port')
    parser.add_argument('--debug', dest='debug', action='store_true')
    parser.set_defaults(feature=False)

    parser.add_argument('--host', '-ho', default='localhost',
                        help='using host')

    args = parser.parse_args()

    if args.mode == "stand_alone" or args.mode == "paint_server":
        print('GPU: {}'.format(args.gpu))
        painter = cgi_exe.Painter(gpu=args.gpu)

    httpd = http.server.HTTPServer((args.host, args.port), MyHandler)
    print('serving at', args.host, ':', args.port, )
    httpd.serve_forever()

欢迎来GitHub讨论:

https://github.com/pfnet/PaintsChainer/issues/150
微信打赏支付宝打赏

感谢您的支持!

文章来源:卡米雷特的小站www.kamilet.cn)转载请注明出处。

卡米雷特

视觉控&技术控,不断学习中!

您可能还喜欢...

发表评论

电子邮件地址不会被公开。 必填项已用*标注