Транспонирование больших и узких изображений в C

Я пытаюсь обработать большие изображения.pgm на языке C. Сначала изображения читаются в формате Image, в виде матриц элементов без знака:

struct Matrix{
  int rows;
  int cols;
  unsigned char * data;
  int widthStep;
};
typedef struct Matrix Image; 

Я использую следующую функцию для чтения изображений с помощью netpbm (netpbm/pam.h):

Image * loadPBM(char * fname){
  FILE * file;
  struct pam inpam;
  tuple * tuplerow;
  unsigned int row;
  Image * image;
  int aux;

  file=fopen(fname,"r");
  pnm_readpaminit(file, &inpam, /*PAM_STRUCT_SIZE(tuple_type)*/ 
  sizeof(struct pam));

  printf("Reading image\n"); 

  /* allocating image*/
  image=(Image*)malloc(sizeof(Image));
  image->cols=inpam.width;
  image->rows=inpam.height;
  image->widthStep=image->cols;
  aux=image->cols & 0x3;
  if (aux!=0){
    image->widthStep+=4-aux;
  }
  image->data=(unsigned char *)malloc(image->widthStep*image->rows);

  tuplerow = pnm_allocpamrow(&inpam);

  for (row = 0; row < inpam.height; row++) {
    unsigned int column;
    pnm_readpamrow(&inpam, tuplerow);
    for (column = 0; column < inpam.width; ++column) {
      unsigned int plane;
      for (plane = 0; plane < inpam.depth; ++plane) {
        image->data[image->widthStep*row+column]= tuplerow[column][plane];
          }
        }
      } 

  pnm_freepamrow(tuplerow);
  fclose(file);
  return image;

}

После прочтения изображения переводятся в формат ImageF, чтобы можно было обрабатывать элементы как двойные, например:

struct MatrixF{
  int rows;
  int cols;
  double * data;
  int widthStep;
};
typedef struct MatrixF ImageF;

Перевод изображения в ImageF:

for (int i = 0; i < in_img->rows; ++i){

    for (int j = 0; j < in_img->cols; ++j){

        in_aux->data[i*(in_img->cols)+j] = (double)in_img->data[i*(in_img->cols)+j];
    }
}

Для фактической обработки изображений мне нужно транспонировать изображения, поэтому я написал следующую функцию:

void transpose(ImageF *in_re, ImageF *out_re){

    int rows = in_re->rows;
    int cols = in_re->cols;

    for(int i = 0 ; i < rows ; ++i){

        for(int j = 0 ; j < cols ; ++j){

            out_re->data[j*rows+i] = in_re->data[i*cols+j];
        }
    }

    out_re->rows = in_re->cols;
    out_re->cols = in_re->rows;

    out_re->widthStep = out_re->cols * sizeof(double);
}

После транспонирования изображения переводятся обратно из ImageF в Image, чтобы сохранить результаты (преобразовать double в unsigned char):

    double val;
    for (int i = 0; i < out_aux->rows; i++){

        for (int j = 0; j < out_aux->cols; j++){

            val = out_aux->data[i*out_aux->cols + j];///((in_img->rows)*(in_img->cols ));

            if (val < 0)
                val = 0.0;
            else if (val > 255)
                val = 255.0;
            out_img->data[i * out_aux->cols + j] = (unsigned char)val;
        }
    }

И, наконец, они хранятся с использованием следующей функции:

void savePBM(char * fname, Image * image){
  FILE * file;
  struct pam outpam;
  tuple * tuplerow;
  unsigned int row;

  int aux;

  file=fopen(fname,"w");
  outpam.file=file;
  outpam.size=sizeof(struct pam);
  outpam.len=sizeof(struct pam);
  outpam.format=RPGM_FORMAT;
  outpam.plainformat=0;
  outpam.height=image->rows;
  outpam.width=image->cols;
  outpam.depth=1;
  outpam.maxval=255;
  strcpy(outpam.tuple_type,PAM_PGM_TUPLETYPE);

  pnm_writepaminit( &outpam );

  printf("Writing image\n");

  tuplerow = pnm_allocpamrow(&outpam);

  for (row = 0; row < outpam.height; row++) {
    unsigned int column;
    for (column = 0; column < outpam.width; ++column) {
      unsigned int plane;
      for (plane = 0; plane < outpam.depth; ++plane) {
    tuplerow[column][plane]=image->data[image->widthStep*row+column];
      }
    }
    pnm_writepamrow(&outpam, tuplerow); 
  } 

  pnm_freepamrow(tuplerow);
  fclose(file);
}

Я считаю, что выделение памяти в памяти было сделано правильно после загрузки входного изображения с помощью loadPBM, как таковой:

    out_img = (Image *)malloc( sizeof(Image) );
    out_img->rows = in_img->cols;
    out_img->cols = in_img->rows;
    out_img->widthStep = out_img->cols * sizeof(unsigned char);
    out_img->data = (unsigned char *)malloc( (out_img->rows)*(out_img->cols)*sizeof(unsigned char) );

/*Auxiliary variables*/
in_aux = (ImageF *)malloc(sizeof(ImageF));
    in_aux->rows = in_img->rows;
    in_aux->cols = in_img->cols;
    in_aux->widthStep = in_aux->cols * sizeof(double);
    in_aux->data = (double *)malloc( (in_aux->rows)*(in_aux->cols)*sizeof(double) );

out_aux = (ImageF *)malloc(sizeof(ImageF));
    out_aux->rows = in_img->rows;
    out_aux->cols = in_img->cols;
    out_aux->widthStep = out_aux->cols * sizeof(double);
    out_aux->data = (double *)malloc( (out_aux->rows)*(out_aux->cols)*sizeof(double) );

По какой-то причине это прекрасно работает для квадратных изображений или даже изображений с разрешением около 450x700. Но когда изображения становятся уже (например, 170x500), этот алгоритм перестает работать правильно. Изображения становятся искаженными, и я понятия не имею, почему, поскольку это работает для других неквадратных матриц, которые просто не так узки. Если кто-нибудь может увидеть, где я ошибся или у него есть какой-либо совет или что-то еще, это будет очень ценно!

Заранее спасибо!

1 ответ

Решение

После некоторого размывания и обсуждения с хорошим другом, мы выяснили, что заполнение, вставленное функцией loadPBM, для выравнивания памяти, в этом разделе:

 aux=image->cols & 0x3;
  if (aux!=0){
    image->widthStep+=4-aux;
  }

Заставляло запись байтов в каждом элементе матрицы идти за борт к следующему элементу, для изображений, ширина которых не кратна 4, искажая их из-за изменения их widthStep. Что объясняет, почему алгоритм работал для некоторых неквадратных матриц, но не для всех. Надеюсь, что это поможет тем, кто видел этот пост и интересовался, почему это происходит. Вы можете просто удалить этот добавленный отступ, и он будет работать как шарм

Другие вопросы по тегам