今日も微速転進

ここではないどこかへ

財務省の公開した交渉記録PDFをいじる その1(目次のPDF編)

自分でモロに忘れそうなので備忘録として。

一時ファイルがじわじわと増殖するのでもっとスマートに行きたいところです。

作戦というか方針

まずは目次のPDFファイルから。

  1. 目次のPDFから交渉記録(応接記録)を機械可読(Computer Readable)な形式に変換
  2. マスク無しのPDFを画像化、再度PDFに変換して過去記事で紹介したAPIOCR
  3. 目次のページ番号から必要なページを割り出して、OCR結果を分割、どうにして添付資料のページを除去
  4. どうにかしてMarkdown
  5. 静的ページジェネレーターでWebページ化

この記事では「1. 」の部分。

問題は黒塗り部分。特定の操作で外せるのはいいけど、外してからOCRして明らかにやばい部分だけ伏せ字にする?

とりあえずやっていきます。

データの入手

財務省のWebサイトから、といいたいところですがそこはやはりWebアーカイブから黒塗りの外せる方を。

srad.jp

  • 20180523p-1.pdf
  • 20180523p-2.pdf
  • 20180523p-3.pdf
  • 20180523p-4.pdf

目次のOCR処理

目次のデータは素直に財務省のページから。

a244.hateblo.jp

20180523p-0.pdfというファイルです。

目次ファイル

一応、目次のPDFファイルについてもGoogle Cloud Vision OCROCRしてみました。残念ながら罫線が邪魔なのか、OCR結果がいまいち。通し番号の列が認識できていない。また、右端のページ番号も認識に失敗している。

水平方向の行を列ごとに細切れにされて出力されて扱いにくい。

Web APIでいくならMicrosoftAPIの法が良いかも。画像を切り出してTesseractでOCRしてみます。

このファイル、不思議なことに各ページが横に長い短冊状の画像が縦に37個結合したPDFになっていて理解不能。 何これ一体?

ファイルサイズを小さくしたかったの?

何はともあれ、表の罫線があるとGoogleOCR API、Tesseract両者とも結果がイマイチであるという点を考慮し、画像をぶった切ってTesseractでOCRする方向で。

本気で行くなら罫線と文字は接触していないので、ハフ変換で直線検出してやればいいのかな。

ちなみに"OpenCV line remove"でググると結構ヒットする。

罫線(水平線)の除去についてはLeptonicaによるサンプルがあります。

Removing dark lines from a light pencil drawing — Leptonica Documentation v1.68 documentation

画像の切り出し

まず目次のPDFから各ページを画像に変換。

$ mutool draw -o "index_page_%d.png" -r 300  pdf_masked/20180523p-0.pdf

-oで出力ファイルを指定。%dはページ番号が入ります。

GUIでやってもいいですが、表紙を除いた合計8ページから日付の列とページ番号の列を切り出せばよいのでコマンドで。

300dpiで画像化した場合、3ページ目以降は表の上端までの距離が概ね150ピクセル、表の見出しを含めると250ピクセルぐらい。

ものすごくざっくり欲しい列の位置を図で示しておく*1

f:id:atuyosi:20180602180107p:plain:w480

カラープロファイルを指定しないと下記のエラーが出るので、+profile "icc"オプションを追加。-noiseオプションでノイズ除去。

convert: profile 'icc': 'RGB ': RGB color space not permitted on grayscale PNG `output2_1.png' @ warning/png.c/MagickPNGWarningHandler/1744.

参考:ImageMagick で PNG の形式を変換 - awm-Tech

左側の日付の列を切り出す。

$ convert masked/index_page_2.png +profile "icc" -noise 4   -crop "480x2980+500+410" output2_1.png

同じように右側の列。

$ convert masked/index_page_2.png +profile "icc" -noise 4 -crop "180x2980+2000+410" output2_2.png

-cropオプションの引数は、切り抜くサイズ(横、縦)とx座標のオフセット、y座標のオフセット。

f:id:atuyosi:20180602204910p:plain f:id:atuyosi:20180602204916p:plain

理想を言えば罫線を除去したいところだがまあ妥協。

3ページ目以降。

$ convert masked/index_page_3.png +profile "icc" -noise 4 -crop "480x3100+500+280" output3_1.png
$ convert masked/index_page_3.png +profile "icc" -noise 4 -crop "180x3100+2000+280" output3_2.png

もう少し範囲を絞って縦の罫線をきっちり除去すべきだったか。

ImageMagick逆引きコマンドリファレンス

ImageMagick逆引きコマンドリファレンス

ImageMagickの日本語の本、あったのか(絶版)。

Tesseract 4.x

ビルドに必要なライブラリのインストール。

$ brew install --only-dependencies tesseract

最新版をソースからビルドしようかと一瞬だけ思いましたが、地味に面倒なので中途半端ですがHomebrew経由で最新版を。要するに4.x系でやりたかったというだけです。

Tesseract本体(開発版)。

$ brew install tesseract  --HEAD

学習用のツールはインストールせず。

現状のHomebrewパッケージの場合、--HEADオプションを付けると全言語の言語別データをダウンロードする。その際、ダウンロードされるのは以下のリポジトリのデータとなるので注意。

github.com

現状、tessdataは無印(3.0x用のデータを含む)、tessdata_fasttessdata_bestの3系統。

なお、Tesseract tessdata downloaderという補助ツールも提供されている。

github.com

4.x系向けのデータはengとかjpnのデータと、script(書字系?)別のデータ(EnglishとかJapaneseとか)で2種類ある。

現状、Japanese.traineddataなどscriptフォルダ直下のデータはそのままでは読み込まれないので、tessdataフォルダにコピーするかシンボリックリンクを作成する必要がるので注意。

今回の画像データで試した範囲ではjpn..traineddataで十分かな。Japanese.traineddataでは半角数字の前後で余計な空白が入る。

目次のOCR

$ tesseract output2_1.png ocr_text  -l jpn

引数は、

tesseract 画像ファイル 出力ファイルの接頭辞 オプション

出力ファイル名にstdoutを指定すると標準出力(つまりコンソール画面)に結果を出力する。

認識結果の例

$ tesseract output2_1.png stdout  -l jpn --psm 6
Warning. Invalid resolution 0 dpi. Using 70 instead.
平成25年6月28日
平成25年7月8日
平成25年7月25日
平成25年8月1日
平成25年8月13日
平成25年8月21日
平成25年9月2日
平成25年9月12日
平成25年10月4日
平成25年10月24日
平成25年10月27日
平成25年10月30日
平成25年11月4日
平成25年11月19日
平成26年12月16日
平成25年12月26日
平成25年12月27日
平成26年1月9日
平成26年1月27日
平成26年1月28日
平成26年1月31日
平成26年2月3日
平成26年2月13日
平成26年3月4日
平成26年3月6日
平成26年3月2日
平成26年4月15日

大変結構。

$ tesseract output2_2.png stdout -l jpn --psm 6 --oem 3
Warning. Invalid resolution 0 dpi. Using 70 instead.
]
18
19
19
22
24
26
27
35
36
42
44
46
47
50
53
55
56
63
64
73
75
77
78
81
84
87

数字の"1"の認識ミスは許容範囲内。

--psm (page segmentation mode)の値によっては最初の”1"を取りこぼすか、余計な空行が入ったりする。古いバージョンのTesseractのほうが数字の認識に関しては優秀だろうと思う。面倒なので試していない。

やはり面倒なのでスクリプトで一括。

#! /usr/local/bin/bash


convert masked/index_page_2.png +profile "icc" -noise 4   -crop "480x2980+500+410" cropped_2_1.png
convert masked/index_page_2.png +profile "icc" -noise 4 -crop "180x2980+2000+410" cropped_2_2.png

for i in `seq 3 9` ; do
    convert masked/index_page_${i}.png +profile "icc" -noise 4   -crop "480x3100+500+280" cropped_${i}_1.png
    convert masked/index_page_${i}.png +profile "icc" -noise 4 -crop "180x3100+2000+280" cropped_${i}_2.png
done


for j in `seq 2 9`; do
    echo "first ${j}"
    tesseract cropped_${j}_1.png ocr_text_${j}_1  -l jpn --psm 6 
    echo "2nd ${j}"
    tesseract cropped_${j}_2.png ocr_text_${j}_2  -l jpn --psm 6 
done

認識ミスは以下の通り。

  • 1ページ目 数字の"1"の認識ミス
  • 3ページ目 「平成26年6月17晶」。末尾の「日」を認識できず。
  • 4ページ目 末尾の「日」の認識ミス。余計な文字「|」
  • 5ページ目 末尾に余計な文字「409 .」
  • 6ページ目 余計な文字「征」「 .」。末尾の「日」の認識ミス。
  • 7ページ目 余計な文字「|」
  • 8ページ目。なぜか"810"が脱落。

あとは「日」が「昌」になったり「目」になっている箇所がいくつか。画像を拡大する処理が必要だったかな。

まあ手作業で修正できる範囲です。

末尾に^L(Form Feed: \f)がくっついているので後で除去する。

OCR結果のテキストファイルの結合

結合するファイル同士の行数が同じであるという前提で、pasteコマンドを使う。

#! /usr/local/bin/bash

for j in `seq 2 9`; do
    echo "target ${j}"
    paste ocr_text_${j}_1.txt  ocr_text_${j}_2.txt >> ocr_result_${j}.tsv 
done

前述の邪魔な^Lを除去する。

$ cat $(ls *.tsv) | tr -d '\f' | sed '/^[[:blank:]]*$/d' > page_list.tsv

参考:Linux パターン別ファイルの空行削除(除外)方法 | 俺的備忘録 〜なんかいろいろ〜

完成品:財務省の公開した国有地の取引に関する交渉記録の目次PDFから抜き出した日付とページ番号の対応表(2018年5月)

※ 不備があったので更新しています。

まとめ

とりあえず目次データをTSV化するところまで。

Tesseractのドキュメント構造解析処理がアレなのはともく、GoogleAPIも罫線入りの表が苦手だとはさすがに意外。

Cloud Vision APIの方は"TEXT_DETECTION"の方を試すのもありかもしれないですがそれはまた別の機会に。

次は本文データ(OCR済み)に挑みます。

続きます。

詳解 OpenCV 3 ―コンピュータビジョンライブラリを使った画像処理・認識

詳解 OpenCV 3 ―コンピュータビジョンライブラリを使った画像処理・認識

シェルプログラミング実用テクニック

シェルプログラミング実用テクニック

*1:いかにもやっつけ仕事

Google Cloud Vision APIで画像メインのPDFから直接OCRする(PDF/TIFF Document Text Detection)

この記事で紹介しているAPI今のところベータ版です。正式リリースまでに仕様が変化する可能性があります。

[2018/09/04 追記]

ドキュメントの翻訳はまだのようですが2018年7月24日付でGAリリースになっています。

PDF、TIFFともに2000ページまで。

[追記 ここまで]

件のPDFデータの処理*1のため、Google Cloud Vision APIOCR機能のうち、PDFおよびTIFF画像を対象にした一括処理モードを試してみました。

新しいモードではなく、従来のDOCUMENT_TEXT_DETECTIONの対応ファイル形式が一括処理モード限定で増えたという感じです。

PDF/TIFF Document Text Detection  |  Cloud Vision API Documentation  |  Google Cloud

いつの間にか追加されており、存在は知っていましたがなんか面倒な雰囲気だったので。

感想としては対象がPDFなら便利、かも。ただ出力形式はfullTextAnotation形式なのでパースがちょっと面倒な印象。APIのバージョンが新しい分、出力は同一ではないです。

注意点など

過去記事でも紹介しているように、Google Cloud Vision APIOCR機能は指定するパラメーターがTEXT_DETECTIONとDOCUMENT_TEXT_DETECTIONの種類あります。このPDFおよびTIFF形式画像処理モードは後者の方で、処理結果ののJSONもfullTextAnotationキーの形式です。

つまり、文書の構造を解析した上で、ページ、ブロック、文章、文字、という階層構造で結果が返ってきます。パースがちょっとめんどいです*2。注意点は座標情報が正規化された数値(最大値が1?)になっている点。対象ファイル形式がPDF/TIFF形式だからというよりは、APIのバージョンの関係だと思いますが。

なお、TIFF形式もPDF同様に複数ページを1ファイルに含めることが可能なファイルフォーマットです*3。この記事ではPDFのOCRしか試していません。

a244.hateblo.jp

この機能は現状ではあくまでもベータ版です。料金体系は既存のAPIの、複数画像一括モードと同じとのこと。

Googleのサンプルコードについて

下記のサンプルコードは90秒以内に処理が完了しないとタイムアウトおよび処理未完了の例外が発生して終了する仕様なので不便。これをベースに改造してみます。

python-docs-samples/detect_pdf.py at master · GoogleCloudPlatform/python-docs-samples · GitHub

パラメーターが決めうちになっていて一度に2ページずつOCR処理が実行される。タイムアウトの値が90秒となっている。

そのままでもそれなりに役に立つ。実際に使うなら以下のパラメータを適当に修正する。

  • batch_size = 2
  • timeout=90

ページ数が多いと時間内に終わらないのでスクリプト自体は異常終了する。ただし、一度リクエストを受け付けてしまえば別に途中でプログラムが落ちても処理は続行されるので問題はない。

なお、他にもGoogle Cloud Vision APIPythonのサンプルコードはいろいろあります。

準備

APIの有効化とかその辺は適当にググって下さい。

環境

改造するサンプルを入手

以下のようにしてサンプルコードを入手。

$ wget https://raw.githubusercontent.com/GoogleCloudPlatform/python-docs-samples/master/vision/cloud-client/detect/detect_pdf.py

Google Storageに出力先を確保

Cloud Vision APIを有効にしたプロジェクトを選択した状態でバケットを作成する。

ストレージ バケットの作成  |  Cloud Storage ドキュメント  |  Google Cloud

ここに対象のPDFをアップロード。完了したら「一般公開で共有する」というカラムのチェックボックスをオン。

権限が適切なら一般公開は不要なはずです。

バケット名とオブジェクト名(ファイル名)を確認しておく。

ライブラリのインストール

$ pip3 install --upgrade google-cloud-vision google-cloud-core

バージョンが古いとベータ版のAPIを実行できないので注意。

改造したプログラム

OCR処理が完了するまでスリープし続けるように修正したPYthonスクリプト

処理の完了待ちをresult()メソッドからdone()の戻り値をチェックする方式に変更している。あとはメソッドの分割。

ハードコードしているバッチサイズ(batch_size = 2)を2から5に変更。

まれにJSONのパースに失敗して例外を投げて終了することがありますが、ちゃんと調べていません。すいません。

実行

クレデンシャル(APIキーの記載されたファイル)のパスを環境変数にセットしておく。direnvを使うと管理がラクでいいのでおすすめ。

$ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/api.json"

参考:いまさら direnv の解説

実行例

基本的な使い方は改造前と同じ。まず作成したバケットにPDFファイルまたはTIFFファイルを配置。引数にGoogle Storageのファイルのパスと、出力先のパスを指定して実行。

$ python3 ocr_and_wait.py --gcs-source-uri gs://<bucket-name>/input.pdf --gcs-destination-uri gs://<bucket-name>/ocr_output/

出力先については末尾のスラッシュが無いとフォルダ名扱いされず、ファイル名の接頭辞扱いになるので注意する。また、出力先のフォルダが存在しない場合は自動的に作成される。

出力ファイル名はoutput-1-to-5.jsonのような開始ページ番号と出力ページ番号を含む形式。ソートしにくい。

JSONのパース

JSONからページ全体の認識結果を取り出すには以下のようにする。

import json

filename = "output-1-to-5.json"
fp = open(filename)
data = json.load(fp)
data['responses'][0]['fullTextAnnotation']['text']

出力のJSONから各ページのそれぞれの文字にアクセスするには、以下のように階層をたどる。

data['responses'][0]['fullTextAnnotation']['pages'][0]['blocks'][0]['paragraphs'][0]['words'][1]['symbols'][0]['text']

もしくはGoogleのサンプルコードがやっているように、json_format.Parse()を使用してパースする。

from google.cloud import vision_v1p2beta1 as vision
from google.protobuf import json_format

json_str = open(filename).read()
response = json_format.Parse(json_str, vision.types.AnnotateFileResponse())

response.responses[0].full_text_annotation.text

同じように、ドキュメント構造を考慮してテキストをたどることもできる。認識したテキストの位置情報や確からしさも取得できる。

response.responses[0].full_text_annotation.pages[0].blocks[0].paragraphs[0].words[0].symbols[0].text  # 出力は省略

以下のように言語や位置の情報も取得できる。

response.responses[0].full_text_annotation.pages[0].blocks[0].paragraphs[0].words[0].symbols[0].property  # 検出した言語
detected_languages {
  language_code: "ja"
}

行末や空白も情報としては取得できる。

response.responses[0].full_text_annotation.pages[0].blocks[0].paragraphs[0].words[0].symbols[1].property
detected_break {
  type: SPACE
}
response.responses[0].full_text_annotation.pages[0].blocks[0].paragraphs[0].words[0].bounding_box
normalized_vertices {
  x: 0.1596638709306717
  y: 0.026128266006708145
}
normalized_vertices {
  x: 0.2218487411737442
  y: 0.027315914630889893
}
normalized_vertices {
  x: 0.2218487411737442
  y: 0.04156769439578056
}
normalized_vertices {
  x: 0.1596638709306717
  y: 0.04156769439578056
}

Symbolクラスの要素(symbolsプロパティ以下の配列の値)にもbounding_boxというプロパティはあるけど値はセットされていない。

response.responses[0].full_text_annotation.pages[0].blocks[0].paragraphs[0].words[0].symbols[0].confidence  # 確からしさ
0.9800000190734863

JSONを自力でパースするよりはGoogleのライブラリを使うほうが少しは楽か?。

そのほか

JSONファイルの分割など

例えば、複数ページ1ファイルなので分割したい時とか。

バッチサイズを1にすればいいだけな気もするけど、すでに処理済みのデータが有る場合の話。

JSONを読み込んでページごとに分割するとして、Googleのサンプルコードのようにjson_format.Parse()でデータを読み込んだ場合、そのままではJSONに戻せない。

読み込んだデータはgoogle.cloud.vision_v1p2beta1.types.AnnotateFileResponseクラスのオブジェクトになっている。

JSONに戻したい場合は、google.protobuf.json_formatモジュールに含まれるMessageToJsonという関数を使う。

はじめから普通にJSONとしてロードすればこういう面倒事は発生しない……。

JSONを分割するスクリプト

以下、参考。

#! /usr/bin/env python3
# encoding: utf-8

from google.cloud import vision_v1p2beta1 as vision
from google.protobuf import json_format
from google.protobuf.json_format import MessageToJson

import json
import pathlib
from natsort import natsorted

## JSONファイルのあるパスを指定
#pl = pathlib.Path("./from_ministry-of_finace/output")
pl = pathlib.Path("./masked/from_ministry-of_finace/index_data/")

filelist = list(pl.glob('*.json'))

for file_path in filelist:
    print(file_path)


    with file_path.open(mode='rt',encoding='utf-8') as jfp:
        str = jfp.read()

        response = json_format.Parse(str, vision.types.AnnotateFileResponse())

        for i, res in enumerate(response.responses) :
            page_num = res.context.page_number

            print("{0} {1} {2}".format(file_path.name, i,page_num ) )

            filename = "output_json_{0}.json".format(page_num)


            ## 出力先のパスを指定
            #output_path = pathlib.Path("ocr_result_masked", filename)
            output_path = pathlib.Path("./index_output", filename)

            with output_path.open(mode='w',encoding='utf-8') as output:

                serialized = MessageToJson(res)

                temp = json.loads(serialized)
                json.dump(temp, output, ensure_ascii=False, indent=2, sort_keys=True, separators=(',', ': '))
                #output.write(serialized)

ブロック単位でOCR結果を表示する例

あまりスマートではないですが参考程度に。

for block in json_data['fullTextAnnotation']['pages'][0]['blocks'] :
    print(block['boundingBox'])
    print(block['blockType'])

    for pg in block['paragraphs'] :
       words = [ w for w in pg['words'] ]
       symbols = []
       for w in words :
           symbols = [ s for s in w['symbols'] ]

            text = []
            for s in symbols :
               text.append(s['text'])
               if "detectedBreak" in s["property"] :
                   node = s["property"]['detectedBreak']
                   if node['type'].startswith('EOL_SURE_SPACE'):
                      text.append("\n")
                   elif node['type'].endswith('SPACE') :
                      text.append(" ")
                   else:
                      text.append("\n")

            print("".join(text), end='')
        print("")

貼り付けるときにミスってインデントがおかしい可能性が微レ存。

参考URL

リクエストとレスポンスに関しては 以下のドキュメントを参照。

まとめ

妙に長い記事になってしまった。ものすごく書きぶりが変な感じだけどまあいいか。

下書き状態で貯め込むよりはいいでしょうってことで。

他の記事も含めて要書き直し。

*1:陸自じゃなくて財務省の方。

*2:多重ループになるのと、空白や改行の場合は処理を分ける必要がある

*3:正直に言うとTesseract の学習時にもマルチページのTIFFファイルが生成されるまで知らなかった

厚生労働省ブラック企業リスト5月版(2018年)

今月はデータ自体が公開されないのかと思いましたよ……。

月末にギリギリで出してくるとは。

a244.hateblo.jp

またこのネタかって感じもしますが当面は続けます。

データの入手元

www.mhlw.go.jp

ファイルは労働基準関係法令違反に係る公表事案というリンクから。ただし、毎月同じファイル名で更新される点に注意。

いつもどおりですが、今月は労働局によってはデータが更新されていないっぽいです。最終更新日が3月のままの労働局(都道府県)がいくつかあります。

機械可読データ(タブ区切りテキスト形式)

厚生労働省ブラック企業リスト(2018年05月31日版) · GitHub

PDFからTSVへの変換スクリプト

Convert PDF to TSV ( for Japan's MHLW illegal company list ) rev. 2

先月と同じで変更なしです。

エキスパートPythonプログラミング改訂2版

エキスパートPythonプログラミング改訂2版

前月との比較

先月の件数が470件で今月は458件。差分は12件(減少)。

福井、長野、愛知の各労働局が+4件と頑張っています。愛知はプラマイゼロですが……。

年度が変わったのでちょっとまごついているのでしょうか。

労働局名 当月公表件数 前月公表件数 新規掲載 掲載終了 差分
北海道労働局 23 25 3 5 -2
青森労働局 8 8 0 0 +/-0
岩手労働局 11 11 0 0 +/-0
宮城労働局 5 5 0 0 +/-0
秋田労働局 4 4 0 0 +/-0
山形労働局 4 4 0 0 +/-0
福島労働局 10 10 0 0 +/-0
茨城労働局 4 5 0 1 -1
栃木労働局 8 8 0 0 +/-0
群馬労働局 5 4 1 0 +1
埼玉労働局 10 11 1 2 -1
千葉労働局 10 10 0 0 +/-0
東京労働局 22 21 1 0 +1
神奈川労働局 9 9 0 0 +/-0
新潟労働局 11 15 1 5 -4
富山労働局 5 5 0 0 +/-0
石川労働局 6 6 0 0 +/-0
福井労働局 8 5 4 1 +3
山梨労働局 12 11 1 0 +1
長野労働局 19 15 4 0 +4
岐阜労働局 12 14 1 3 -2
静岡労働局 8 10 0 2 -2
愛知労働局 31 31 4 4 +/-0
三重労働局 11 10 1 0 +1
滋賀労働局 7 7 0 0 +/-0
京都労働局 8 8 1 1 +/-0
大阪労働局 28 33 2 7 -5
兵庫労働局 17 18 1 2 -1
奈良労働局 5 5 0 0 +/-0
和歌山労働局 9 9 0 0 +/-0
鳥取労働局 2 2 0 0 +/-0
島根労働局 7 8 1 2 -1
岡山労働局 7 7 0 0 +/-0
広島労働局 17 16 1 0 +1
山口労働局 4 4 0 0 +/-0
徳島労働局 5 5 0 0 +/-0
香川労働局 0 0 0 0 +/-0
愛媛労働局 12 13 0 1 -1
高知労働局 7 8 0 1 -1
福岡労働局 15 14 1 0 +1
佐賀労働局 6 6 0 0 +/-0
長崎労働局 4 5 0 1 -1
熊本労働局 12 13 0 1 -1
大分労働局 11 11 0 0 +/-0
宮崎労働局 2 2 0 0 +/-0
鹿児島労働局 7 8 0 1 -1
沖縄労働局 10 11 0 1 -1

北海道労働局の掲載企業で、過去に一度掲載されなくなった起業で、今頃再掲載されている企業がある。何があったんだろう。

まとめ

手抜きが感が半端ない記事になりましたが……。

まさか公表終了かと思いましたが継続のようです。求人詐欺とか内定取り消し、過労死についても企業名を公表して欲しいと思いますけどね。

通称働き方改革法案が衆議院で可決されたとかで、ちょっとは改善してほしいところですが、高プロにばかり注目されてインターバル規制とか全然話題にならないし。

労働者側にメリットのある話と高プロのような毒饅頭をセットにしなければいいのに……。

メディアの報道の仕方も良くないのかな?

労基署がやってきた! (宝島社新書)

労基署がやってきた! (宝島社新書)

PDFからコマンド操作で画像を取り出す方法

財務省やらかし案件がらみで。PDFを画像化するのではなく、内部に含まれる画像を取り出そうという話です。

財務省の公開しているPDF,差し替え前の黒塗りが外せる状態のPDFが入手できるとのこと。

security.srad.jp

これはOCRやり直しか……。

ページ数とPDF内の画像数は一致しているので、どうやら画像の上に黒い四角形をPDFの描画命令で重ねているっぽいです。

PDFを画像化するのではなく、PDFに1ページ1画像ずつ含まれている画像データを抜き出すと、黒塗り前の画像がそのまま入手できます。

文書をスキャンして、何かのツールで黒い四角形を重ねて、その後PDF出力の際にPDF出力用のソフトが下地とマスクを結合せずにPDF化したんでしょうか。

再発防止のためにも詳細を知りたいところ。

黒塗りが外せる方のPDFのメタデータによると、 "Creator"、”Producer"ともに、"Adobe Acrobat Standard DC 15.6.30413"。

ちなみに差し替え前のマスクが外せるファイルはPDFバージョン1.6。差し替え後のファイルはPDFバージョン1.5。面白い。

なお、スキャンした画像データの正しい黒塗りの仕方としては、黒くマスクしてレイヤー情報のない画像形式として出力して、そのあとPDF化すべきだったのかな。

Adobe Acrobat DCがあるなら、画像化する前に機密情報の削除ツールを使うべきだったのでは……。

墨消しツールの使用方法 (Acrobat Pro DC)

詳細な手順:ASCII.jp:PDFファイルの見せたくない部分を完璧に墨で消す方法|柳谷智宣がAdobe Acrobat DCを使い倒してみた

前置きは以上。

PDFからの画像の取り出し

QPDF、Coherent PDFには画像抽出オプションはない。QPDFの方はできそうな気はするけど、バイナリデータの状態になってしまうと思う。

PythonのライブラリでやるならPDFMinerか。PDFのオブジェクトストリームだか何かの圧縮を外さないといけないと思う。

Popplerの付属コマンド

Popplerの付属コマンド、pdfimagesを使う。

PopplerをインストールするとおまけでPDF関連コマンドがついてくる。 mac環境ならHomebrew経由でインストールできる。

$ brew install poppler

デフォルトの出力形式はpbm形式。-png-tiffオプションで出力形式を変更できる。最後の引数に出力ファイルの接頭辞を指定する。スラッシュ区切りのパスを指定すると特定フォルダ以下に取り出した画像ファイルが保存される。

$ mkdir output
$ pdfimages -png input.pdf output/image

上記の場合、 出力ファイル名のファイル名はimage-000.pngという形で先頭からの通し番号(ゼロからスタート)がつく。変更したい場合は-pオプションをつけるとPDF側のページ番号を付きで出力される。

$ pdfimages -p -png  20180523p-1.pdf output/image

出力ファイル名はimage-001-000.png

画像は白黒反転しているのでImageMagickあたりで変換する。

参考:さくっと色反転するためのコマンド - Qiita

mutool (mupdf)

mac環境でのインストールはコマンド一発。

$ brew install mupdf-tools

画像の抽出は、

$ mutool extract filename.pdf

または、

$ mutool extract filename.pdf 画像のオブジェクト番号

出力ファイル名はimg-1315.pngのようになる。1315という数字はページ番号ではなくPDF内部での画像データのオブジェクト番号。

この場合、PDF内部でのオブジェクト番号を含んだファイル名となり、対応関係が非常にわかりにくい。

PDF内部でのオブジェクト番号と、ペー内での並び順は下記のコマンドで確認できる。

$ mutool info filename.pdf

なお、このツールでも抽出した画像は白黒反転している。

番外編

WebサービスでPDFから画像を取り出すものがある。あとは素直にAdobe Acrobat Pro DC。

tools.pdf24.org

ベルリンの会社が運営している?

まとめ

いろいろあるだろうと思ったら結局、Popplerとmutoolか。

まあ普通に外せましたよ、財務省様。誰がどういう経緯で気づいたんだろう。とりあえず交渉記録しかゲットしてませんが、他のもあるのか?

めんどいから流石に手を出しませんが。

後でもうちょっと読みやすく書き直す、かも。

音に関する信号処理についてのプログラミングの本いろいろ(たぶん随時更新)

音声かそれ以外かは問わない。

信号処理の本を買えばいいはずなんだけど、理論の本だとプログラム化するノウハウが載ってなさそうだし、ハードウェアの話をされても困る。

ハッキリとプログラミングについて説明しているであろう書籍だけピックアップ。

サウンドプログラミングとかオーディオプログラミングとか、信号処理とか。

MATLABとか商用ソフトなり特定のハードウェアを要求するものは除く。順不同。

絶版になっている本は中古価格が高め。スマートスピーカーなどの音声関連に注目が集まっているからか。

技術評論社から出版されているものが一番入手難易度が低い。

著者別その1

三上 直樹という方はInterfaceにオーディオ関係で記事を書いている人なので内容の信頼性については問題はないと思う。

はじめて学ぶディジタル・フィルタと高速フーリエ変換

実物を見ていないのでなんとも言えず。

『C#によるデジタル信号処理プログラミング』

絶版なのか中古価格高め。京都市の図書館にはある。

この本は著者の作成したライブラリを使うことが前提。手を動かして理論の理解を助けるのが目的っぽい。ただ、理論の解説は中途半端な印象。

そもそもWaveファイルのフォーマットの解説がない。標本化定理などデジタル信号処理の基本的な理論は理解している前提で、実際にコードに落とし込めるようになりたいという人向けだと思う。

プログラム自体の解説はわかりやすい。読者にどの程度のプログラミングの経験を想定しているのか、中途半端。C#ならではの解説というよりは、WindowsのGUI環境だからこそという感じがする。想定読者がちょっとぶれている感じが否めない。

最後の方にFFTの実装が載っている。C#のライブラリにFFTが無いとは思えないが、要件によっては参考になるはず。

ただ、付録CDのソースコードの商用利用の可否については明記されていない。
出所を明示すれば改変版の再配布は問題ないと書いてある。

著者別その2

サウンドエフェクトのプログラミング―Cによる音の加工と音源合成

サウンドエフェクトのプログラミング―Cによる音の加工と音源合成

実物を見ていないのでなんとも言えず。

著者別その3

『C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理』

C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理

C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理

アマゾンのレビューはまあまあ良さげ。実物を見ていない。

サウンドプログラミング入門

サウンドプログラミング入門――音響合成の基本とC言語による実装 (Software Design plus)

サウンドプログラミング入門――音響合成の基本とC言語による実装 (Software Design plus)

サポートページ:サウンドプログラミング入門

新しい方は技術評論社の書籍だけど電子書籍版がない。

Windows環境でborland c++ compilerを使うことを前提にしている。

標本化定理の説明から入っている。電子音・電子楽器向けという印象。個人的には最後の章が一番参考になった。

正直に言うと、途中の章は興味がなかったので流し読みしかしていない。

コンパイラの提供元が消滅しているのでダウンロードはちょっと面倒。写経とかはしていない。

Borland C++ Compilerのダウンロード及びインストール | Borland C++ Compiler 5.5のインストール

雑誌のバックナンバー

Interface (インターフェース) 2016年 6月号

Interface (インターフェース) 2016年 6月号

中古の場合は付録のCDの有無に注意。未入手。

CD-ROM版 Interface 2016(音声信号処理プログラム全集付属)

CD-ROM版 Interface 2016(音声信号処理プログラム全集付属)

総集編の方はCDが付属するのは初回限定と書かれている。

その他

あと内容は薄そうだけど以下も(Pythonの本だけど内容が偏っているみたいに見える)。

Pythonで学ぶ実践画像・音声処理入門

Pythonで学ぶ実践画像・音声処理入門

まとめ

実物を確認した範囲では、おすすめは「サウンドプログラミング入門」かな。何がしたいか、使うツールは何かによるとしかいえませんが。

これ一冊で全部OKという本はないみたいです。とにかく動くものが作りたいのか、詳しく勉強したいとか、方向性とどの程度の専門知識(バックグラウンド)があるかで いいと思う本は全然違ってくるのでは。

Qiitaの記事でも何でもいいのでまずは手を動かして、その上でよくわからないキーワードなりトピックについて専門書で勉強するのがいいかな、と。

広告