zoukankan      html  css  js  c++  java
  • 五彩斑斓的黑

    五彩斑斓的黑

    项目背景

    由于众所周知的原因(武汉2020),只能在家整东西玩,想起了以前和同学聊天提到的五彩斑斓的黑,遂来了灵感,造出来这么一个轮子。

    项目效果图

    项目简介

    五彩斑斓的黑,可以把黑白的论文变成五彩斑斓,这样看论文的时候就不无聊了(误

    实现了pdf转化为五颜六色的pdf,其实对于其他类型图片的处理也是一样的

    项目开源地址

    我的github,求路过的朋友点个star吧,提PR那是更好的!

    项目依赖

    1. 必备:opencv 图像处理的基础库
    2. 必备:wand 是imagemagick的前端
    3. 必备:imagemagick 基础库
    4. 选配:flask 用于搭建一个服务器在线批量转换

    算法介绍

    读取pdf

    借助wand可以实现pdf转换为jpg图片形式,这样方便使用opencv处理:

    def parse_pdf(filepath, resolution=300):
        pdf = wi(filename=filepath, resolution=resolution)
        pdf = pdf.convert("jpeg")
        return pdf
    

    转换完毕的pdf批量保存为文件,因为没有找到wand与opencv的联通格式,所以采用文件作保存处理。

    def save_pdf_as_img(pdf, filename):
        page_count = 1
        for img in pdf.sequence:
            page = wi(image=img)
            page.save(filename=filename + str(page_count) + '.jpg')
            page_count += 1
        return page_count
    

    生成彩色图像

    这里偷了个懒,利用等差数列乘以一个等比数列,采用opencv的热力图applyColorMap()方法变为彩色,再横竖相加获取随机但仍有一定规律的彩虹图像。

    @memoize
    def get_color_img(width, height):
        w = np.logspace(0, 255, height, base=1.01, dtype=np.uint8)
        w = np.reshape(w, (-1, 1))
        h = np.linspace(0, 255, width, dtype=np.uint8)
        h = np.reshape(h, (1, -1))
        shu = w * h
        shu = np.reshape(shu, (height, -1))
        shu = cv2.cvtColor(shu, cv2.COLOR_GRAY2BGR)
        shu = cv2.applyColorMap(shu, cv2.COLORMAP_HSV)
    
        w = np.linspace(0, 255, height, dtype=np.uint8)
        w = np.reshape(w, (-1, 1))
        h = np.logspace(0, 255, width, base=1.01, dtype=np.uint8)
        h = np.reshape(h, (1, -1))
        heng = w * h
        heng = np.reshape(heng, (height, -1))
        heng = cv2.cvtColor(heng, cv2.COLOR_GRAY2BGR)
        heng = cv2.applyColorMap(heng, cv2.COLORMAP_HSV)
    
        img = heng + shu
    
        img = cv2.medianBlur(img, 101)
        return img
    

    可以看出,使用权重比较大的中值滤波很好的平滑了图像。

    因为替换模版的不变性,所以我们利用python的装饰器在内存中保存这个图像,具体可能我会写一篇关于python高级特性:装饰器的文章,不过还是有可能咕咕咕了,这里就把装饰器理解为一个参数是函数的函数就好了。

    def memoize(func):
        cache = dict()
    
        def memoized_func(*args):
            if args in cache:
                return cache[args]
            result = func(*args)
            cache[args] = result
            return result
    
        return memoized_func
    

    图像的混合

    利用蒙版(mask)技术,可以完美的实现黑色文字的替换,或者可以自行更改这个要替换的颜色,或者颜色范围,以实现更花里胡哨的效果。

    def mix_img(file_dir, filename, count):
        path = os.path.join(file_dir, filename)
        for i in range(1, count):
            img = cv2.imread(path + str(i) + '.jpg')
            color = get_color_img(img.shape[1], img.shape[0])
            mask = (img == (0, 0, 0))[:, :, 0]
            img[mask] = color[mask]
            mix = img
            cv2.imwrite(path + str(i) + '.jpg', mix)
    

    pdf的生成

    混合完的图像还是利用wand复原为pdf文件,生成的pdf可能会比较大,因为变成了纯图像。

    def save_img_as_pdf(file_dir, filename, count, output_dir, output_filename):
        path = os.path.join(file_dir, filename)
        output_path = os.path.join(output_dir, output_filename)
        with wi() as w:
            for i in range(1, count):
                with wi(filename=path + str(i) + '.jpg') as page:
                    w.sequence.append(page)
            w.save(filename=output_path)
        return output_path
    

    网络端接受上传

    首先让我们5秒写个网页来接受用户pdf文件的输入:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>五彩斑斓的黑 - Licsber</title>
        <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
        <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
        <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
    </head>
    <body class="container">
    <div class="jumbotron">
        <h3 id="header">{{message}}</h3>
        <h2>欢迎试用一键五彩斑斓的黑,支持上传pdf。</h2>
        <h2>创意By Licsber、Mikewang000000。</h2>
        <form id="form1" method="post" action="/api/upload" enctype="multipart/form-data">
            <div>
                <input id="File1" type="file" name="myfile" class="btn btn-default btn-lg">
                <button type="submit" class="btn btn-info btn-lg">提交</button>
            </div>
        </form>
    </div>
    </body>
    <script>
        var heading = $("#header")[0];
        setInterval(function () {
            if (heading.style.display == "block") {
                heading.style.display = "none";
            } else if (heading.style.display == "none") {
                heading.style.display = "block";
            }
        }, 1000);
    </script>
    </html>
    

    闪烁部分是后来写的,主要网页表单部分真的只写了五秒。

    serve一下主页:

    @app.route('/')
    def upload_test():
        message = ''
        return render_template(UPLOAD_HTML, message=message)
    

    然后写一个flask的函数来接收文件并保存,这个函数,嘿嘿,发现了什么东西没,不管符不符合都先保存再说,方便日志记录。

    @app.route('/api/upload', methods=['POST'], strict_slashes=False)
    def api_upload():
        f = request.files['myfile']
        if f:
            old_name = f.filename
            ext = old_name.rsplit('.', 1)[1]
            unix_time = int(time.time())
            new_filename = old_name + str(unix_time) + '.' + ext
            print(new_filename)
            path = os.path.join(file_dir, new_filename)
            f.save(path)
        else:
            message = '你没上传文件哦'
            return render_template(UPLOAD_HTML, message=message)
        if f and allowed_file(f.filename):
            pdf = entity.Pdf(file_dir, new_filename, OUTPUT_PATH)
            return downloader(pdf.get_output_filename())
        else:
            message = '文件类型不支持哦 重新上传试试呢'
            return render_template(UPLOAD_HTML, message=message)
    

    下载文件就简单了,flask自带这个方法。

    @app.route("/download/<path:filename>")
    def downloader(filename):
        dir_path = os.path.join(app.root_path, 'output')
        return send_from_directory(dir_path, filename, as_attachment=True)
    

    足够的抽象

    Java程序员表示,看见什么都想给它抽象成一个类:

    class Pdf:
        def __init__(self, file_dir, filename, output_path):
            self.file_dir = file_dir
            self.filename = filename
            self.pdf = pdf.parse_pdf(filepath=os.path.join(file_dir, filename))
            self.page_count = 0
            self.output_path = output_path
    
        def extract(self, tmp_dir='tmp/'):
            return pdf.save_pdf_as_img(pdf=self.pdf, filename=tmp_dir + self.filename)
    
        def convert(self):
            if self.page_count == 0:
                return
            color.mix_img(file_dir='tmp/', filename=self.filename, count=self.page_count)
            return
    
        def save(self):
            return pdf.save_img_as_pdf(file_dir='tmp/', filename=self.filename,
                                       count=self.page_count, output_dir=self.output_path, output_filename=self.filename)
    
        def get_output_filename(self):
            self.page_count = self.extract()
            self.convert()
            self.save()
            return self.filename
    

    本来想随机填充的,发现随机生成的图像虽然随机,但是不好看。

    TODOS

    1. 异步返回处理结果(因为算法有点慢
    2. 使用OSS减轻网络io负担
    3. 更多文件图片格式支持

    后记

    还有什么好玩的想法可以私聊我呀(
    大家试着可以一起实现一下

  • 相关阅读:
    多线程博文地址 http://www.cnblogs.com/nokiaguy/archive/2008/07/13/1241817.html
    vs2010一运行就报错deven.exe assert failure 解决方法,卸载系统中.netFramework最新版本的(简体中文)
    Lambda语句中创建自定义类型时,也可指定某种特定类型,方法是在new与{}之间写上类型名称
    Win7开始菜单所在目录
    C#中Struct与Class的区别
    Linq语句:三表联查
    用exp、dmp导入导出用户到同一个实例下时,类型type会有问题
    列、约束重命名,原数据不丢失
    CDM中,创建一个或多个组合属性的唯一约束
    EF中新建表和关联表的方法
  • 原文地址:https://www.cnblogs.com/licsber/p/colorful-black.html
Copyright © 2011-2022 走看看