五彩斑斕的黑


五彩斑斕的黑

項目背景

由於眾所周知的原因(武漢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. 更多文件圖片格式支持

后記

還有什么好玩的想法可以私聊我呀(
大家試着可以一起實現一下


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM