C言語で画像の切り抜き・トリミング

このページでは、画像のトリミングについての説明と、そのプログラムの例の紹介を行います。

画像のトリミング

まず画像のトリミングについて説明します。

画像のトリミングとは、ある座標からある座標への領域内の画像部分を切り取り、切り取った部分のみの画像を生成する処理です。

下の図がイメージつきやすいと思います。

画像のトリミングのプログラム

さっそく画像のトリミングを行うプログラムを紹介していきたいと思います。

スポンサーリンク

ソースコード

下記が画像のトリミングを行うプログラムのソースコード例になります。

main.c
#include "myJpeg.h"

int main(int argc, char *argv[]){

  BITMAPDATA_t bitmap, trimmedBitmap;
  int x, y, c;
  int sx, sy, ex, ey;
  char outname[256];

  if(argc != 2){
    printf("ファイル名指定してください\n");
    return -1;
  }

  if(jpegFileReadDecode(&bitmap, argv[1]) == -1){
    printf("jpegFileReadDecode error\n");
    return -1;
  }

  printf("開始点のX座標は?");
  scanf("%d", &sx);
  if(sx < 0 || sx >= bitmap.width - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }
  printf("開始点のY座標は?");
  scanf("%d", &sy);
  if(sy < 0 || sy >= bitmap.height - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }
  printf("終了点のX座標は?");
  scanf("%d", &ex);
  if(ex < 0 || ex >= bitmap.width - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }
  printf("終了点のY座標は?");
  scanf("%d", &ey);
  if(ey < 0 || ey >= bitmap.height - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }

  /* 座標の大小関係が逆の場合はエラー */
  if(sx > ex) {
    printf("X座標の開始点と終了点の大小関係が逆です\n");
    return -1;
  }
  if(sy > ey) {
    printf("Y座標の開始点と終了点の大小関係が逆です\n");
    return -1;
  }

  trimmedBitmap.width = ex - sx + 1;
  trimmedBitmap.height = ey - sy + 1;
  trimmedBitmap.ch = bitmap.ch;
  printf("width = %d, height = %d, ch = %d\n", trimmedBitmap.width, trimmedBitmap.height, trimmedBitmap.ch);

  trimmedBitmap.data = (unsigned char*)malloc(sizeof(unsigned char) * trimmedBitmap.width * trimmedBitmap.height * trimmedBitmap.ch);
  if(trimmedBitmap.data == NULL){
    printf("maloc err\n");
    return -1;
  }

  /* ここから画像処理 */
  for(y = 0; y < trimmedBitmap.height; y++){
    for(x = 0; x < trimmedBitmap.width; x++){
      for(c = 0; c < trimmedBitmap.ch; c++){
        trimmedBitmap.data[((y * trimmedBitmap.width) + x) * trimmedBitmap.ch + c]
        = bitmap.data[(((y + sy) * bitmap.width) + (x + sx)) * bitmap.ch + c];
      }
    }
  }

  /* ここまで画像処理 */

  sprintf(outname, "%s", "trimmed.jpg");

  if(jpegFileEncodeWrite(&trimmedBitmap, outname) == -1){
    printf("jpegFileEncodeWrite error\n");
    freeBitmapData(&bitmap);
    return -1;
  }

  freeBitmapData(&trimmedBitmap);
  freeBitmapData(&bitmap);

  return 0;
}

jpegFileReadDecodejpegFileEncodeWriteBITMAPDATA_t などは下記ページで公開している myJpeg.cmyJpeg.h で定義していますので、コンパイルを行いたい方は事前にコピペして入手してきてください。

libjpegの使い方解説ページアイキャッチ【C言語】libjpegのインストールと使用方法・使用例

コンパイルと実行

例えば下記コマンドによりソースコードをコンパイルし、実行可能ファイルを作成することができます。

gcc myJpeg.c trimming.c -o main.exe -ljpeg

gcc に指定する各ファイルは下記のようになります。

  • main.c:このページで公開しているソースコード
  • myJpeg.c:JPEG 関連の関数を定義したソースコード
  • myJpeg.h:JPEG 関連の構造体等を定義したソースコード
  • main.exe:作成される実行可能ファイル

-ljpeg は libjpeg へのリンクを行うためのオプションです。

コンパイルを行う場合は libjpeg のインストールが必要になりますので、まだインストールしていない方は下記ページ等を参考にして事前に libjpeg をインストールしておいてください。

libjpegの使い方解説ページアイキャッチ【C言語】libjpegのインストールと使用方法・使用例

myJpeg.cmyJpeg.h も上記ページで公開しています。

実行時には、トリミングを行いたい JPEG 画像へのファイルパスを指定します。

例えば main.exe と同じフォルダにある input.jpg をトリミングするのであれば下記のようにして引数を指定して main.exe を実行します。

./main.exe inut.jpg

実行すると、下記のようにトリミングの開始点や終了点の入力が促されますのでトリミングしたい座標を指定すれば、トリミング後の JPEG ファイル trimmed.jpgmain.exe と同じフォルダに作成されます。

./main.exe input.jpg 
width = 640, height = 427, ch = 3
開始点のX座標は?100
開始点のY座標は?100
終了点のX座標は?200
終了点のY座標は?200
width = 101, height = 101, ch = 3

プログラムの説明

それでは先ほど紹介したソースコードのプログラムについて解説をしていきたいと思います。

スポンサーリンク

トリミング領域の取得

下記でトリミングする領域を指定できるようにしています。

この指定された座標 (sx, sy) と座標 (ex, ey) を基にトリミングを行います。

より具体的には入力画像の座標 (sx, sy) から座標 (ex, ey) の領域をトリミングします。座標 (ex, ey) も含まれる点に注意してください。

一応座標が画像の外にある場合はエラー終了するようにしています。

トリミング領域の指定
  printf("開始点のX座標は?");
  scanf("%d", &sx);
  if(sx < 0 || sx >= bitmap.width - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }
  printf("開始点のY座標は?");
  scanf("%d", &sy);
  if(sy < 0 || sy >= bitmap.height - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }
  printf("終了点のX座標は?");
  scanf("%d", &ex);
  if(ex < 0 || ex >= bitmap.width - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }
  printf("終了点のY座標は?");
  scanf("%d", &ey);
  if(ey < 0 || ey >= bitmap.height - 1) {
    printf("座標がおかしいです\n");
    return -1;
  }

JPEGファイルの読み込み・書き込み

jpegFileReadDecode 関数と jpegFileEncodeWrite 関数はそれぞれ JPEG を読み込む関数・JPEG を保存する関数(JPEG の書き込みを行う関数)になります。

詳細は下記ページで解説していますので、詳しく知りたい方は下記ページをご参照ください。

libjpegの使い方解説ページアイキャッチ【C言語】libjpegのインストールと使用方法・使用例

トリミング後画像のメモリ確保

トリミング後画像は元画像に比べてサイズが小さくなります。

指定された sxsyexey に基づいて画像サイズを計算し、その画像サイズの分だけ malloc 関数でメモリ領域を確保しています。

トリミング後画像用のメモリ確保
  trimmedBitmap.width = ex - sx + 1;
  trimmedBitmap.height = ey - sy + 1;
  trimmedBitmap.ch = bitmap.ch;
  printf("width = %d, height = %d, ch = %d\n", trimmedBitmap.width, trimmedBitmap.height, trimmedBitmap.ch);

  trimmedBitmap.data = (unsigned char*)malloc(sizeof(unsigned char) * trimmedBitmap.width * trimmedBitmap.height * trimmedBitmap.ch);
  if(trimmedBitmap.data == NULL){
    printf("maloc err\n");
    return -1;
  }

スポンサーリンク

トリミング処理

実際のトリミング処理は下記のループの中で行っています。

トリミング処理
  for(y = 0; y < trimmedBitmap.height; y++){
    for(x = 0; x < trimmedBitmap.width; x++){
      for(c = 0; c < trimmedBitmap.ch; c++){
        trimmedBitmap.data[((y * trimmedBitmap.width) + x) * trimmedBitmap.ch + c]
        = bitmap.data[(((y + sy) * bitmap.width) + (x + sx)) * bitmap.ch + c];
      }
    }
  }

xy はトリミング後画像上の座標であり、それぞれ x は横方向、y は縦方向の座標としています。

一方、mn は元画像上の座標であり、それぞれ m は横方向、n は縦方向の座標としています。

ループの中で行っているのは、トリミング後画像の座標 (x, y) に対して元画像の座標 (x + sx, y + sy) の画素のコピーです。

これをトリミング後画像の幅・高さ(幅:ex - sx、高さ:ey - sy)分行なっていますので、指定された開始点と終了点の間の領域のみがコピーされ、結果的にトリミング後画像を得ることができます。

トリミング後の画像

最後にこのページで紹介したプログラムでトリミングした結果を紹介しておきます。

・トリミング前(画像サイズ364 x 500)

・トリミング後(開始点[82, 100]、終了点[282, 400]でトリミング)

まとめ

このページではC言語で画像をトリミング・切り抜きする方法とそのプログラム例について解説しました。

複数枚の画像を完全に同じサイズに切り抜きするときなどにかなり使えますのでぜひ参考にしていただければと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です