Como Combinar Imagens com C++ e ImageMagick

⚽ Nesse exemplo juntamos em 2 linhas e 3 colunas com 6 imagens de uma simulação do Campeonato Brasileiro.


Como Combinar Imagens com C++ e ImageMagick


O Campeonato Brasileiro de futebol está se aproximando do fim, ou seja, faltam só 5 rodadas e muita gente quer saber quem será rebaixado(além do Sport, é claro 😃 ) e eu pedi pra o DeepSeek simular os próximos jogos e ele me disse os resultados de todos os jogos e eu preenchi no site do Globo Esporte na opção de Simulação e fiz uma captura de tela de todas as rodadas após recortar todas as seis imagens com resolução iguais: 605x405 pixels.

Então, decidi combinar as imagens em 3 colunas e 2 linhas para que coubesse em uma única imagem para eu postar nas mídias sociais para que todos pudessem ver em uma única imagem todos os resultados e a tabela final em uma única imagem.

E para isso criei um código com C++ e ImageMagick.


Código

Se quiser testar na sua máquina, antes lembre-se de ter instalado o ImageMagick Dev(API .h), para isso rode:

sudo apt install imagemagick graphicsmagick-libmagick-dev-compat

As 6 imagens que usei foram essas aqui, faça download de cada uma delas para o testar o código:

  • 1.jpg Brasileirão Rodada 34

  • 2.jpg Brasileirão Rodada 35

  • 3.jpg Brasileirão Rodada 36

  • 4.jpg Brasileirão Rodada 37

  • 5.jpg Brasileirão Rodada 38

  • 6.jpg Brasileirão Tabela Final

Crie um arquivo, por exemplo: brasileirao.cpp e insira esse código:

#include <Magick++.h>
#include <print>

int main(int argc, char** argv){
  const std::string filename = "resultado.jpg";
  Magick::InitializeMagick(*argv);

  const int cols = 3, rows = 2, imgw = 605, imgh = 405, pad = 5;
  const Magick::Color bg_color("blue");

  std::vector<Magick::Image> imagens;
  for(int i = 1; i <= 6; ++i){
    std::string nome = std::to_string(i) + ".jpg";

    try{
      Magick::Image img;
      img.read(nome);
      img.resize(Magick::Geometry(std::to_string(imgw) + "x" + std::to_string(imgh)));
      imagens.push_back(img);
    }catch(const std::exception& e){
      std::println("{}: {}", nome, e.what());
      return EXIT_FAILURE;
    }
  }

  int totalw = cols * imgw + (cols - 1) * pad;
  int totalh = rows * imgh + (rows - 1) * pad;

  Magick::Image combined(Magick::Geometry(totalw, totalh), bg_color);
  combined.matte(true);

  size_t index = 0;
  for(int r = 0; r < rows; ++r){
    for(int c = 0; c < cols; ++c){
      if (index >= imagens.size()){
        break;
      }

      int x = c * (imgw + pad);
      int y = r * (imgh + pad);

      combined.composite(imagens[index], x, y, Magick::OverCompositeOp);
      ++index;
    }
  }

  combined.write(filename);
  std::println("{}: {}x{}", filename, totalw, totalh);
}

Se não tiver o C++23, substitua o std::println por std::cout.

Compile:

g++ $(Magick++-config --cxxflags --cppflags) brasileirao.cpp $(Magick++-config --ldflags --libs)

E depois é só rodar o ./a.out que será gerada a imagem: resultado.jpg com as 6 imagens combinadas. Igual a essa abaixo que foi gerada assim:

Resultado Brasileirão

Clique na imagem para vê-la em alta resolução, ela possui: 1825x815.

Se quiser alterar para usar com suas imagens, altere:

  • imgw e imgh pela largura e altura das suas imagens, respectivamente;
  • cols e rows de acordo como deseja, note(no código) que no meu caso fiz 2 linhas e 3 colunas;
  • Você também pode alterar o padding(espaço entre as imagens), modificando a variável: pad. E entre outras coisas que você desejar.

Se quiser usar um Makefile:

CC       = g++ -std=c++23
DEBUG    = -g
OPT      = -O1
WARN     = -Wall -Werror
INCFLAGS = `Magick++-config --cxxflags --cppflags`
CCFLAGS  = $(DEBUG) $(OPT) $(WARN)
LDFLAGS  = `Magick++-config --ldflags --libs`
FILES    = brasileirao.cpp
all:
	$(CC) $(CCFLAGS) $(INCFLAGS) $(FILES) $(LDFLAGS)
	@rm -f *.o

Ou um CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(brasileirao CXX)

# Captura CFLAGS do Magick++
execute_process(
    COMMAND Magick++-config --cxxflags --cppflags
    OUTPUT_VARIABLE RAW_MAGICKPP_CFLAGS
)

# Captura LDFLAGS do Magick++
execute_process(
    COMMAND Magick++-config --ldflags --libs
    OUTPUT_VARIABLE RAW_MAGICKPP_LIBS
)

# Limpeza: remove tabs, quebras de linha e espaços excessivos
string(REPLACE "\n" " " MAGICKPP_CFLAGS "${RAW_MAGICKPP_CFLAGS}")
string(REPLACE "\t" " " MAGICKPP_CFLAGS "${MAGICKPP_CFLAGS}")
string(REPLACE "\n" " " MAGICKPP_LIBS "${RAW_MAGICKPP_LIBS}")
string(REPLACE "\t" " " MAGICKPP_LIBS "${MAGICKPP_LIBS}")

string(STRIP "${MAGICKPP_CFLAGS}" MAGICKPP_CFLAGS)
string(STRIP "${MAGICKPP_LIBS}" MAGICKPP_LIBS)

# Agora separa corretamente
separate_arguments(MAGICKPP_CFLAGS)
separate_arguments(MAGICKPP_LIBS)

add_executable(brasileirao brasileirao.cpp)

target_compile_features(brasileirao PRIVATE cxx_std_23)
target_compile_options(brasileirao PRIVATE -g -O1 -Wall -Werror)

target_compile_options(brasileirao PRIVATE ${MAGICKPP_CFLAGS})
target_link_libraries(brasileirao PRIVATE ${MAGICKPP_LIBS})

Rode: cmake -B build . && cmake --build build && ./build/brasileirao

Lembrando que se você usar o Valgring pode obter um falso-positivo, para evitar isso, use a flag: OMP_NUM_THREADS=1, assim:

OMP_NUM_THREADS=1 valgrind --leak-check=full --track-origins=yes ./a.out

A saída final será: ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0).

Só mais uma dica, para usar o ImageMagick no seu LSP inclua no seu ~/.config/clangd/compile_flags.txt a linha:

-I/usr/include/GraphicsMagick

Ou opcionalmente:

magick++ --cppflags >> .config/clangd/compile_flags.txt
# Ou
pkg-config --cflags Magick++ >> .config/clangd/compile_flags.txt


É uma dica simples, mas que além de ser útil é um bom exercício C++ 😃 .


Veja também:

Fui!


cpp magick futebol


Compartilhe


Nosso canal no Youtube

Inscreva-se


Marcos Oliveira

Marcos Oliveira

Desenvolvedor de software
https://github.com/terroo


Crie Aplicativos Gráficos para Linux e Windows com C++

Aprenda C++ Moderno e crie Games, Programas CLI, GUI e TUI de forma fácil.

Saiba Mais

Receba as novidades no seu e-mail!

Após cadastro e confirmação do e-mail, enviaremos semanalmente resumos e também sempre que houver novidades por aqui para que você mantenha-se atualizado!