ImageMagick/GraphicsMagick: 임시 파일 없이 한 단계에서 여러(3+) 이미지를 병합/합성하는 방법은 무엇입니까?

ImageMagick/GraphicsMagick: 임시 파일 없이 한 단계에서 여러(3+) 이미지를 병합/합성하는 방법은 무엇입니까?

텍스트/글꼴/크기가 다른 여러 개(2개 이상)의 작은 이미지를 만든 다음 이를 임시 이름의 파일을 직접 만들지 않고 한 단계로 다른 위치의 단일 소스 이미지에 오버레이하려고 합니다.

지금까지 시도한 내용은 다음과 같습니다.

  • 단일 텍스트를 생성하고 파이프 및 MIFF 스트림을 사용하여 어딘가에 병합합니다.(작동하지만 여러 이미지가 필요합니다):

    gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- \
    | gm composite -geometry +10+10 miff:- source.tif out.tif
    
  • 첫 번째 이미지 생성 및 병합, 출력 임시 파일 저장, 임시 파일 로드 및 소스에 병합(작동하지만 gm필요한 임시 파일에 대한 두 번의 호출을 작성해야 하며 수동으로 삭제해야 함):

    gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- \
    | gm composite -geometry +10+10 miff:- source.tif tmp.tif ; \
    gm convert \
      -background transparent \
      -fill grey \
      -font Calibri \
      -size 200x50 \
      -pointsize 12 \
      -gravity SouthEast \
      label:'small text' \
      miff:- \
    | gm composite -geometry +300+100 miff:- tmp.tif out.tif
    
  • 여러 개의 텍스트를 생성하고 마지막 위치만 계산되어 모든 파일에 적용되는 하나의 위치로 병합합니다.(작동하지 않습니다. 첫 번째 텍스트만 덮어쓰게 됩니다.):

    { gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- ; \
      gm convert \
      -background transparent \
      -fill grey \
      -font Calibri \
      -size 200x50 \
      -pointsize 12 \
      -gravity SouthEast \
      label:'small text' \
      miff:- ; } \
    | gm composite miff:- -geometry +10+10 miff:- -geometry +300+100 source.tif -geometry +0+0 out.tif
    
  • 구문을 사용하여 ( ... )"text1.tif" 및 "text2.tif"를 gm convert중괄호 안의 명령 으로 바꿉니다.(어쨌든 작동하지 않습니다. 어쩌면 잘못 사용하고 있는 것일까요?)

  • flatten대신 사용composite (작동하지만 모든 파일이 다른 위치가 아닌 동일한 위치에 있는 경우 여러 페이지 이미지가 깨집니다.)
  • append단일 파이프를 통해 여러 파일 사용(나란히 겹치지 않고 겹쳐져야 함)

임시 파일을 저장하고 불러오지 않고도 3개 이상의 이미지를 서로 결합하여 각 이미지가 출력 이미지에서 서로 다른 위치를 갖도록 할 수 있나요? 또는 이미지를 쓰기 전에 겹치는 이미지의 z 순서를 어떻게 설정할 수 있습니까? 가능 gm 1.3.25하다면 bash 4.3실행 가능한 대안에 열려 있습니다.

답변1

원래 요청에 따르면 한 번에 2개의 이미지로만 작업할 수 있으므로 3개의 이미지(파일에서 1개, 생성된 2개)를 한 번에 결합해야 한다는 것을 알고 있습니다.

또한 임시 데이터를 디스크에 저장하고 싶지 않다는 것도 이해합니다. 가장 빠른 해결책은 @nominal-animal의 이전 답변에서 제안한 대로 "tmpfs"를 사용하는 것입니다.

그러나 한 단계 더 나아가면 임시 파일 생성을 방지하고 기본적으로 모든 처리를 한 번에 수행할 수 있습니다. 내가 찾은 해결책은 데이터를 디스크에 저장하지 않고 수신 부분도 준비될 때까지 파이프의 모든 전송을 일시 중지하는 특수 파일인 "명명된 파이프"를 사용하는 것이었습니다. 제공하신 전체 명령(명령, 입력 및 출력만)을 사용하지 않고 필요한 매개변수를 자유롭게 입력하세요.

# First it's necessary to have the pipes created, one time operation and they can be reused over and over.
mknod /tmp/pipe1 p
mknod /tmp/pipe2 p
mknod /tmp/pipe3 p

# generate the 2 images and send each to a pipe 
gm convert ... miff:- > pipe1 &
gm convert ... miff:- > pipe2 &
# merge the source image to the 1st generated image
gm composite ... source.tif pipe1 miff:- > pipe3 &
# finally merge the result to the 2nd generated image
gm composite ... pipe3 pipe2 out.tif

작업이 4단계만 필요한 것처럼 보일 수도 있지만 실제로는 디스크에 아무것도 쓰지 않고 모든 작업이 한 번에 수행됩니다.

작동 방식과 명령이 이런 방식으로 작성된 이유는 다음과 같습니다.

  • 첫 번째 이미지를 생성하는 명령은 요청 처리를 시작하지만 출력은 명명된 파이프로 이동해야 합니다. 지금은 파이프에서 아무것도 읽히지 않으므로 출력이 수신 대기를 중지합니다. 프로세스가 종료되거나 오류가 표시되지 않으므로 계속 진행하기 위해 백그라운드로 전환됩니다.
  • 두 번째 그림은 파이프만 다를 뿐 동일합니다.
  • 소스 이미지와 첫 번째 파이프의 데이터를 병합하면 파이프 1에서 읽고 첫 번째 빌드 명령이 해제되지만 출력이 다른 파이프로 이동하므로 마지막 단계를 기다려야 합니다.
  • Pipe3과 Pipe2의 데이터를 병합하면 작업이 완료되고 필요한 출력 파일이 생성되며 처음 2개의 명령이 릴리스됩니다.

파이프를 파일이 아닌 임시 파일로 생각할 수 있습니다. 백그라운드에 무언가를 넣어두는 것을 잊지 마세요. 그렇지 않으면 스크립트가 Ctrl+C를 기다립니다. 각 명령은 백그라운드에 별도로 배치되어야 합니다! 이전 명령이 완료된 후 출력을 생성하면 이와 같은 스크립트에 출력을 넣는 데 문제가 없습니다.

좋은 기능은 4개 명령 모두 파이프에서 들어오는 데이터에 의존하기 때문에 백그라운드에서 먼저 3개 명령을 시작한 다음 정상적으로 마지막 명령을 시작할 수 있다는 것입니다. 또한 파이프를 추가하는 것만으로 여러 명령으로 쉽게 확장할 수 있습니다.

제한 사항이 있습니다. 동일한 파이프라인을 병렬로 사용할 수 없습니다. 필요한 경우 병렬로 실행되는 각 인스턴스에 대해 서로 다른 파이프라인 그룹을 사용해야 합니다. 그렇지 않으면 놀랄 것입니다. :) 그런 문제는 없습니다. 순차적 처리로.

성능 측면에서 볼 때 이것이 "tmpfs"에 임시 파일을 만드는 것보다 빠른지는 알 수 없으므로 실제 시나리오에서 테스트하여 알아내야 합니다. 이렇게 하려면 4개의 프로세스와 해당 데이터를 메모리에 모두 수용할 수 있을 만큼 충분한 메모리가 있어야 합니다. (이는 처리/생성하는 이미지의 크기에 따라 크게 달라집니다.)

답변2

이는 질문에 대한 답변이 아니라 원래 질문의 전제를 나타내는 여담일 뿐입니다."임시 파일...수동으로 삭제해야 합니다", 완전히 틀렸습니다.

(가능한 경우 이 "답변"을 게시하고 있으며, 이 전제가 거짓임을 지적하고 OP 및 기타 사람들이 임시 파일을 자유롭게 사용할 수 있도록 합니다. 많은 경우 임시 파일을 사용하면 스크립트가 더 강력해지고 유지 관리가 더 쉬워집니다.)

Bash 스크립트가 하나 이상의 임시 파일을 사용해야 하는 경우 항상 다음 방법을 사용합니다.

#!/bin/bash
work="$(mktemp -d)" || exit 1
trap "cd / ; rm -rf '$work'" EXIT

이는 mktemp도우미 프로그램을 사용하여 임시 작업 디렉터리( )를 만듭니다 /tmp/. 사용할 수 없거나 mktemp디렉터리를 만들 수 없으면 스크립트가 중단됩니다.

작업 디렉토리와 여기에 포함될 수 있는 모든 파일을 자동으로 삭제하기 위해 트랩을 설정합니다 EXIT. 이 트랩은 쉘이 종료될 때마다 트리거됩니다. 즉, bash는 정상적으로 또는 오류로 인해 지정된 명령을 실행합니다.

트랩 EXIT명령은 루트 디렉토리로 변경하고(단지 현재 작업 디렉토리가 임시 디렉토리 내부에 있지 않은지 확인하기 위해) 전체 하위 트리를 삭제합니다. 명령이 큰따옴표로 묶여 있기 때문에 $work트랩이 설정될 때 평가됩니다. 이는 work나중에 스크립트에서 변수 값을 변경하더라도 트랩이 원래 임시 디렉터리를 계속 삭제한다는 것을 의미합니다.

트랩이 현재 작업 디렉토리를 변경하더라도 트랩 외부(다른 스크립트나 다른 프로세스)에는 아무런 영향을 미치지 않습니다. 왜냐하면 현재 작업 디렉토리는 각 프로세스에 특정하고 현재 bash 프로세스는 트랩 직후에 종료되기 때문입니다. 트랩 명령이 실행됩니다.

따라서 스크립트가 완료되면 임시 디렉터리 자체뿐만 아니라 아래에 생성된 모든 임시 파일 $work(예 "$work/1": 등)이 자동으로 삭제됩니다."$work/result"$work

관련 정보