今回は、OpenCVの顔検出データセットで、画像から顔を検出して、顔のみをInDesignに配置する記事です。

実用性:Zero

言いたいこと:コードを簡潔(=今後のメンテナンス)にするため:

・組版のjavascript(今回はappscriptで)に、組版部分のみ書く

・組版以外のデータ処理、Pythonのような言語に任せる(今回の例だと、顔の矩形座標検出処理は、10行程度しかない)

OpenCVのインストール:pip install opencv-python

使い方:

  1. InDesign(例はCC2019)に画像を配置、選択
  2. プログラム実行
  3. 新規documentが作成され、その中に顔のみが表示
import os
import cv2  # pip install opencv-python
from appscript import *


# 顔検出haarcascade
frontalface = os.path.join(cv2.data.haarcascades,
                           "haarcascade_frontalface_default.xml")
cascade = cv2.CascadeClassifier(frontalface)


def get_face_rects(input_img, scaleFactor):
    image = cv2.imread(input_img)
    height, width = image.shape[:2]
    faces = cascade.detectMultiScale(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY),
                                     scaleFactor=scaleFactor,  # 顔検出精度
                                     minNeighbors=2,
                                     minSize=(30, 30))
    if len(faces) == 0:
        print("No faces detected.")
        return [], 0, 0
    """
    # draw red rectangles and save image
    color = (0, 0, 255)  # B,G,R !!
    for (x, y, w, h) in faces:
        cv2.rectangle(image, (x, y), (x + w, y + h), color, thickness=2)
        cv2.putText(image, text=f"scaleFactor={scaleFactor}", org=(100, 50),
                    fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                    fontScale=1, color=(255, 0, 0))
    cv2.imwrite(f"__{scaleFactor}_rectangles_".join(
        os.path.splitext(input_img)), image)
    """
    return faces, width, height


def get_img_path_from_indesign():
    # get currently selected obj in InDesign
    sel = indd.selection()[0]  # javascript: app.selection[0]
    if sel.class_() == k.image:  # convert "Direct Selection" to "selection"
        sel.parent.select()
        sel = indd.selection()[0]
    graphic = sel.graphics[1]
    img_path = graphic.item_link.file_path()  # colon delimited(on the Mac OS)
    return img_path.replace("Macintosh HD", "").replace(":", "/")


indd = app("Adobe InDesign CC 2019")
img_path = get_img_path_from_indesign()
rects, width, height = get_face_rects(img_path, scaleFactor=1.1)
if len(rects) != 0:
    indd.copy()  # copy currently selected obj
    doc = indd.make(new=k.document)
    for rect in rects:
        indd.paste()  # paste copied obj
        new_img = doc.selection()[0]
        new_img.fit(given=k.frame_to_content)  # show whole image
        new_img.move(to=[0, 0])
        # height/width (any indesign unit)
        indd_height, indd_width = new_img.geometric_bounds()[2:]
        # convert pixel unit to percentage
        x1, x2 = rect[0] / width, (rect[0] + rect[2]) / width
        y1, y2 = rect[1] / height, (rect[1] + rect[3]) / height
        # convert percentage to indesign unit
        new_img.geometric_bounds.set([y1 * indd_height, x1 * indd_width,
                                      y2 * indd_height, x2 * indd_width])
因みに、顔検出の精度引数scaleFactorを1.09/1.3にしたら、大分違ってくる:
get_face_rects(img_path, scaleFactor=1.09)
get_face_rects(img_path, scaleFactor=1.3)