具体例:
上記、magickのappendを、ディレクトリの中の全ての画像に対して行う関数を作成する。
このスクリプトで"glass/"ディレクトリと"metalic/"ディレクトリ内の連番画像をappendでつなげて"appends/"ディレクトリへ保存する
import subprocess # コマンドラインを実行する import os # パス文字列の操作など import glob # ファイル一覧取得など ############################################ ############################################ ## # @brief 画像データを横に二つ並べる処理を全ての画像に対して行う # @param [in] dir_left 左側に配置する画像が存在するディレクトリ # @param [in] dir_right 右側に配置する画像が存在するディレクトリ # @param [in] dir_out 出力先のディレクトリ def image_append(dir_left , dir_right , dir_out): # 各ディレクトリの中のファイル一覧 L_images = glob.glob(dir_left + "/*.png", recursive=True) R_images = glob.glob(dir_right + "/*.png", recursive=True) # L_images,R_imagesのリストの要素をそれぞれiL,iRとして for(iL,iR) in zip(L_images,R_images): # 拡張子なしの left側のファイル名を取得 oname = os.path.splitext(os.path.basename(iL))[0] # 出力ファイル名作成 opathname = dir_out + "/" + oname + ".png" # コマンド作成 command = "magick convert +append " + iL + " " + iR + " " + opathname # 作成したコマンドの確認 print(command) # コマンドの実行 subprocess.call(command) ############################################ ############################################ # 連番画像作成 引数は全てディレクトリ名 image_append("glass" , "metalic" , "appends")
これ単体ならわざわざPythonから呼び出す必要はないが、上記スクリプトに続けて呼び出したいのでPythonからcallする。
import subprocess # コマンドラインを実行する import os # パス文字列の操作など import glob # ファイル一覧取得など ############################################ ############################################ ## # @brief 連番画像から動画を作成 # @param [in] source 画像の場所とファイル名のパターン # @param [in] outFileName 出力ファイル名 # @param [in] startNumber 画像の連番の開始番号 def mp4out(source,outFileName,startNumber): # コマンド作成 command = f'ffmpeg -start_number {startNumber} -r 15 -i {source} -vcodec libx264 -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -framerate 60 {outFileName}' # コマンドの実行 subprocess.call(command) ############################################ ############################################ # 動画作成 ファイル名、出力ファイル名、連番の最初の番号 mp4out("appends/%04d.png","out.mp4",1)
C:\TEST\DATA │ compmv.py ... pythonスクリプト │ out.mp4 ... ffmpegで生成した動画ファイル。mp4outで生成される │ ├─appends │ 0000.png │ 0001.png │ 0002.png │ 0003.png │ 0004.png │ .... 結合済み画像 image_appendで出力される │ ├─glass │ 0001.png │ 0002.png │ 0003.png │ 0004.png │ .... 連番画像1 │ └─metalic 0001.png 0002.png 0003.png 0004.png .... 連番画像2
import subprocess # コマンドラインを実行する import os # パス文字列の操作など import glob # ファイル一覧取得など ############################################ ############################################
## # @brief 画像データを横に二つ並べる処理を全ての画像に対して行う # @param [in] dir_left 左側に配置する画像が存在するディレクトリ # @param [in] dir_right 右側に配置する画像が存在するディレクトリ # @param [in] dir_out 出力先のディレクトリ def image_append(dir_left , dir_right , dir_out): # 各ディレクトリの中のファイル一覧 L_images = glob.glob(dir_left + "/*.png", recursive=True) R_images = glob.glob(dir_right + "/*.png", recursive=True) # L_images,R_imagesのリストの要素をそれぞれiL,iRとして for(iL,iR) in zip(L_images,R_images): # 拡張子なしの left側のファイル名を取得 oname = os.path.splitext(os.path.basename(iL))[0] # 出力ファイル名作成 opathname = dir_out + "/" + oname + ".png" # コマンド作成 command = "magick convert +append " + iL + " " + iR + " " + opathname # 作成したコマンドの確認 print(command) # コマンドの実行 subprocess.call(command)
############################################ ############################################
## # @brief 連番画像から動画を作成 # @param [in] source 画像の場所とファイル名のパターン # @param [in] outFileName 出力ファイル名 # @param [in] startNumber 画像の連番の開始番号 def mp4out(source,outFileName,startNumber): # コマンド作成 command = f'ffmpeg -start_number {startNumber} -r 15 -i {source} -vcodec libx264 -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -framerate 60 {outFileName}' # コマンドの実行 subprocess.call(command)
############################################ ############################################ # 連番画像作成 image_append("glass" , "metalic" , "appends") # 動画作成 mp4out("appends/%04d.png","out.mp4",1)
ImageMagickで画像サイズを変更、ffmpegで連番画像を動画に変換。
その一連の作業をPythonスクリプトで行う。
# usage: # srcdir dstdir [0/1/2] # 0 ... 連番画像作成 + 動画作成 # 1 ... 連番画像作成 # 2 ... 動画作成 import subprocess import sys import os import shutil import glob #import random ############################################ # コマンドライン引数 args = sys.argv # ディレクトリを二つ取得 dir_in = args[1] dir_out = args[2] mode = int(args[3]) ############################################ # 入力画像一覧 file_name_list = glob.glob(dir_in + "/*.*", recursive=True) if mode==0 or mode==1: index=0 for infile in file_name_list: sindex = str(index).zfill(8) index += 1 # 変換後の画像サイズ xysize = "426x240" # "1920x1080" # 拡張子抜きのファイル名 resultname = os.path.splitext(os.path.basename(infile))[0] # 出力ファイル名作成 outfile = dir_out + "/" + sindex + ".jpg" # ImageMagickのコマンド作成 cmd = "magick " + infile + " -gravity center -background gray -resize " + xysize + " -extent " + xysize + " " + outfile # ImageMagick実行 subprocess.call(cmd) if mode==0 or mode==2: # https://qiita.com/livlea/items/a94df4667c0eb37d859f # 連番画像を0.2fps(5秒で1枚)の動画と考えて、 # それを15fps(1秒に15枚)の動画として作成。結果、5秒間に15x5枚同じ画像を表示する動画となる。 subprocess.call('ffmpeg -framerate 0.2 -i ' + dir_out + '/' + '%08d.jpg' + ' -vcodec libx264 -pix_fmt yuv420p -r 15 out.mp4')
こんなスクリプトを作って、以下のように実行する。
実行結果:
-gravityでセンタリング、-resizeでスケーリング、-extentで切り取る範囲、-backgroundで余白の色。
-resizeはデフォルトでアスペクト比を維持するので、-resizeで指定した画像サイズがそのまま結果となるとは限らない。そこで-extentを併用する。
-resizeによってスケーリングの範囲内に必ず画像が収まるので、結果画像(切り出す範囲)を-extentで指定する。-resizeのw,hよりextentのw,hの方が大きかった部分は、backgroundで塗りつぶされる。
・コメント投稿時、ユーザ名/URI入力欄が表示されないようにする
・コメント表示時、万が一ユーザ名/URIデータがあっても表示されないようにする
入力欄を表示されないようにするため、functions.phpに以下を追記する
<?php /*コメント欄から各項目を削除*/
function hide_comment_fields_author($defaults){ $defaults['fields']['author'] = ''; return $defaults; } add_filter( 'comment_form_defaults', 'hide_comment_fields_author');
function hide_comment_fields_email($defaults){ $defaults['fields']['email'] = ''; return $defaults; } add_filter( 'comment_form_defaults', 'hide_comment_fields_email');
function hide_comment_fields_url($defaults){ $defaults['fields']['url'] = ''; return $defaults; } add_filter( 'comment_form_defaults', 'hide_comment_fields_url');
/*コメント欄から各項目を削除 ここまで*/ ?>
参考サイト。以下では上記処理を一括指定する方法が公開されている。というか、comment_form_defautsに$defaults['fields'][***]を指定する関数を指定しているので一つにまとめられる。
仮に情報としてこれらが入力されていても、表示されないようにする。
まずfunctions.php側でコメント表示時に指定するコールバック関数を作成。
この時、URLや投稿者名を取得して表示する箇所を実装しないでおく。
なおついでアバターの記述も無効化した。
<?php /* comments.phpで呼び出すwp_list_commentsに指定するコールバック関数 */ function mytheme_comment($comment, $args, $depth) { $GLOBALS['comment'] = $comment; ?> <li <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>"> <div id="comment-<?php comment_ID(); ?>"> <div class="comment-author vcard"> <?php /*アバターも無効化*/ /*echo get_avatar($comment,$size='48',$default='<path_to_url>' );*/ ?> <?php /*コメント投稿時指定のURLを非表示*/ /*echo get_comment_author_link( $comment_ID );*/ ?> <?php /*コメント投稿時指定の投稿者名を非表示*/ /*printf(__('<cite class="fn">%s</cite> <span class="says">より:</span>'), get_comment_author())*/ ?> </div> <?php if ($comment->comment_approved == '0') : ?> <em><?php _e('Your comment is awaiting moderation.') ?></em> <br /> <?php endif; ?> <div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ) ?>"><?php printf(__('%1$s at %2$s'), get_comment_date(), get_comment_time()) ?></a><?php edit_comment_link(__('(Edit)'),' ','') ?></div> <?php comment_text() ?> <div class="reply"> <?php comment_reply_link(array_merge( $args, array('depth' => $depth, 'max_depth' => $args['max_depth']))) ?> </div> </div> <?php } /* comments.phpで呼び出すwp_list_commentsに指定するコールバック関数 ここまで*/ ?>
comments.php側では、作成した関数をwp_list_commentsのコールバックに指定する。
<?php if (post_password_required()) { return; } ?> <div id="comments"> <?php if (have_comments()) :?> <h3 id="comments-count"><?php echo get_comments_number().' 件のコメント'; ?></h3> <ul id="comments-list"> <?php wp_list_comments(array( 'avatar_size'=>48, 'style'=>'ul', 'type'=>'all', 'callback'=>'mytheme_comment' )); ?> </ul> <?php if (get_comment_pages_count() > 1) : ?> <ul id="comments-pagination"> <li id="prev-comments"><?php previous_comments_link('<< 前のコメント'); ?></li> <li id="next-comments"><?php next_comments_link('次のコメント >>'); ?></li> </ul> <?php endif; endif; ?> <?php comment_form(); ?> </div><!-- comments -->
参考サイト。
https://deluxeblogtips.com/wordpress-remove-author-link-in/
コメント欄でトラブルがあったのでこれを機に消してしまうことにした。
実際ほぼすべての状況において名前やメールアドレスを「サイト管理者が」持つことは有害無益なので入力欄などないほうがいい。
デコード。webpファイルを読み込み、ppmファイルに変換する。なおアルファ付き画像は扱わない。
#include <fstream> #include "ppmP3_read.hpp" #include <vector> #include <webp/decode.h> #pragma comment(lib,"libwebp.lib") void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p, const int vmax ); int main() { // ファイルの画像の格納先 std::vector<std::uint8_t> image;
/////////////////////////////////// // webp形式の画像データをファイルから読み込む std::ifstream ifs( "C:\\test\\a.webp", std::ios::binary); ifs.seekg(0, std::ios::end); long long int size = ifs.tellg(); ifs.seekg(0); // ファイル読み込み image.resize(size); ifs.read((char*)image.data(), size); //ファイルのデータサイズ const size_t datasize = image.size();
/////////////////////////////////// // 画像データの情報の格納先 int width; int height; uint8_t* rgb = nullptr; // 画像情報を取得する構造体 WebPBitstreamFeatures info; // 画像情報を取得 WebPGetFeatures( image.data(), datasize, &info );
/////////////////////////////////// // 画像情報を表示 printf("alpha : %s\n", info.has_alpha?"true":"false"); printf("format : %s\n", (info.format == 0) ? "undefined": ((info.format == 1) ? "lossy" : "lossless")); printf("animation:%s\n", info.has_animation ? "true" : "false");
/////////////////////////////////// if (info.has_alpha != 0) { // WebPDecodeRGBA でRGBAのデータ列に変換 } else { // RGBのデータ列に変換。戻り値がRGGデータ rgb = WebPDecodeRGB( image.data(), datasize, &width, &height ); }
/////////////////////////////////// ppmP3_write("C:\\test\\fromwebp.ppm", width, height, rgb, 255);
// libWebPが確保したメモリの解放 WebPFree(rgb);
} //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255 //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p, const int vmax ) { FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2] ); k++; } fprintf(fp, "\n"); } fclose(fp); }
libwebpを使ってみる(Encode , Lossless)
小規模な画像をLossyで保存すると色がかなり悲惨なことになる(QualityFactor==100.0でも)。
なのでテストのため少し大きめのPPMを読み込んで変換する。
#include "ppmP3_read.hpp" #include <vector> #include <webp/encode.h> #pragma comment(lib,"libwebp.lib") // https://suzulang.com/ppmp3_read-memalloc-ver/ #include "ppmP3_read.hpp" // https://www.study.suzulang.com/2dcg-functions/nbyte-data-type #include "NByteData.hpp" int main() { using PixelT = NByteData<3>; std::vector<PixelT> rgbdata; // 画像読み込み int width; int height; int vmax; // rgb画像読み込み ppmP3_read( "C:\\data\\b.ppm", &width, &height, &vmax, [&rgbdata](const size_t pixelcount) { rgbdata.resize(pixelcount); return (unsigned char*)&rgbdata[0]; } ); // 非可逆圧縮の品質 0.0f ~ 100.0f float quality_factor = 50.0f; uint8_t* output = 0; // 非可逆圧縮 size_t size = WebPEncodeRGB( rgbdata.data()->data(), width, height, width * 3, // stride 一行のメモリサイズ quality_factor, // 品質 &output ); printf("%zu", size); FILE* fp = fopen("C:\\data\\test-lossy-rgb.webp", "wb"); fwrite(output, 1, size, fp); fclose(fp); WebPFree(output); }
libwebpを使って.webpファイルを出力してみる。
https://developers.google.com/speed/webp/download?hl=ja
展開する。
マニュアルは以下:
https://developers.google.com/speed/webp/docs/api
#pragma warning(disable:4996) #include <vector> #include <webp/encode.h> #pragma comment(lib,"libwebp.lib") int main() { // 作成したwebp形式のデータのアドレスを受け取るポインタ uint8_t* output = 0; // 2×3のRGB画像を作成 std::vector<uint8_t> rgbdata = { 255, 0, 0 /**/, 0,255, 0, 0, 0,255 /**/, 255,255, 0, 255, 0,255 /**/, 0,255,255 }; int width = 2; int height = 3; size_t size = WebPEncodeLosslessRGB( rgbdata.data(), // 変換したい画像へのポインタ width, // 画像幅 ピクセル数 height, // 画像高さ ピクセル数 width * 3, // stride 一行のメモリサイズ &output // データを受け取るポインタのアドレス ); // 戻り値はoutputのバイト数 printf("%zu", size); // ファイル出力 FILE* fp = fopen("C:\\dev\\test-lossless-rgb.webp", "wb"); fwrite(output, 1, size, fp); fclose(fp); // libwebpが確保したメモリの解放 WebPFree(output); }
RGBAも可。
#pragma warning(disable:4996) #include <vector> #include <webp/encode.h> #pragma comment(lib,"libwebp.lib") int main() { uint8_t* output = 0; std::vector<uint8_t> rgbdata = { 255, 0, 0, 255 /**/, 0,255, 0, 212, 0, 0,255, 170 /**/, 255,255, 0, 127, 255, 0,255, 85 /**/, 0,255,255, 42 }; int width = 2; int height = 3; size_t size = WebPEncodeLosslessRGBA( rgbdata.data(), width, height, width * 4, // stride 一行のメモリサイズ &output ); printf("%zu", size); FILE* fp = fopen("C:\\dev\\test-lossless-rgba.webp", "wb"); fwrite(output, 1, size, fp); fclose(fp); WebPFree(output); }
以前作ったFloodFill。無理やり再帰なしにしたが、今度はちゃんとスタックを使ってまともに再帰なし版を書いた。
#pragma once #pragma once #include "NByteData.hpp" #include <stack> using uc3T = NByteData<3>; //! @brief 座標計算などを行う補助クラス class accessor { int width; int height; public: void set(int Width, int Height) { width = Width; height = Height; } bool is_in(const int x, const int y) { if (x < 0 || y < 0 || x >= width || y >= height) return false; return true; } size_t get_pos(const int x, const int y) { return (size_t)y * width + (size_t)x; } };
//! @brief floodfill本体 //! @param [in] x 対象の画素のX座標 //! @param [in] y 対象の画素のY座標 //! @param [in] targetcolor 塗りつぶし対象の色 //! @param [in] replacementcolor 塗りつぶし結果の色 //! @param [in,out] img 対象の画像データ //! @param [in] acc 画素の座標等を求めたりする補助クラスのインスタンス void ff_fill( int x, int y, uc3T targetcolor, uc3T replacementcolor, uc3T* img, accessor* acc) { using xyT = std::pair<int, int>; std::stack<xyT> stack; stack.push({ x,y }); while (stack.empty() != true) { xyT xy = stack.top(); stack.pop(); x = xy.first; y = xy.second; if (acc->is_in(x, y) == false) continue; uc3T* node = &img[acc->get_pos(x,y)]; if (*node != targetcolor) { continue; } *node = replacementcolor; stack.push({ x + 1, y }); stack.push({ x - 1, y }); stack.push({ x, y - 1 }); stack.push({ x, y + 1 }); } return; }
//! @brief floodfillエントリポイント //! @param [in] seedx 塗りつぶし開始点 //! @param [in] seedy 塗りつぶし開始点 //! @param [in] replacementcolor 塗りつぶし結果の色 //! @param [in,out] img 対象の画像データ //! @param [in] Width 画像幅(画素数) //! @param [in] Height 画像高さ(画素数) void f_fill( int seedx, int seedy, uc3T replacementcolor, uc3T* img, int Width, int Height) { accessor acc; acc.set(Width, Height); if (acc.is_in(seedx, seedy) == false) return; uc3T targetcolor = img[acc.get_pos(seedx, seedy)]; ff_fill(seedx, seedy, targetcolor, replacementcolor, img, &acc); }
前回Gantry 5を有効にするまでやったので、各ページごとにスタイル等を設定していく方法を調べた。
まず、設定の変更をわかりやすくするために、メニューを作成する。
カテゴリーアーカイブのページで表示されるメニュー、シングルページで表示されるメニュー、...と言った具合で作成しておく。
次に、アウトラインを作成する。
カテゴリーアーカイブのページ用のアウトライン、シングルページ 用のアウトライン 、...と言った具合で作成しておく。
ここで、一例としてカテゴリアーカイブのアウトラインを、カテゴリアーカイブのページに適用する事を考える。
まず、カテゴリアーカイブページのアウトラインがちゃんとカテゴリアーカイブのページで表示される設定になっている事をわかりやすくするためにメニューを設定しておく。
次に、Categoryarchiveアウトラインを選択した状態で、Assignmentsへ行き、Pate Contextの「Category Archive Page」のスイッチをONにする。
この設定で、Category Archive Pageに対しては、Categoryarchiveアウトラインを使用する、事になる。
この状態で設定を保存しカテゴリアーカイブを見てみると、ちゃんとアウトラインが反映されている。
ここで、例えばカテゴリアーカイブページでは本文を表示させないでタイトル一覧だけにしたいというような変更を加えてみる。
今度はContentへ行き、Archiveの項目の「Content」のスイッチをOFFにする。これでコンテンツ、つまり本文が表示されなくなる。ついでに、「Comments Meta」もOFFにし、No comments という部分も非表示にしてみる。
設定を保存して更新すると、本文が消えているのがわかる。
Gantry 5 はテーマフレームワーク。Wordpress版もあるらしい。
プラグインから検索してインストールもできる(Gantry 5 Framework)のだが、テーマもインストールしなければならず、テーマと本体のバージョンの違いによってはうまく動かないので公式からダウンロードしたほうがいい。
公式からプラグイン本体とテーマを一つ以上ダウンロードする
プラグインとテーマをインストールする
テーマを有効化するとテーマの設定項目が表示される。