Транспонирование больших и узких изображений в C
Я пытаюсь обрабатывать большие .изображения pgm в C. изображения сначала считываются в формате Image, как матрицы беззнаковых элементов char :
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 imagen");
/* 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, так что я могу обрабатывать элементы как double , как таковой:
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 imagen");
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) );
По какой-то причине это прекрасно работает для квадратных изображений или даже изображений с разрешением около 450х700. Но по мере сужения изображения (например, 170х500) этот алгоритм перестает работать корректно. Изображения искажаются, и я понятия не имею, почему, поскольку это работает для других неквадратичных матриц, которые просто не так узки. Если кто-нибудь может увидеть, где я ошибся или имеет какой-то совет или что-то еще, это будет чрезвычайно ценно!
Заранее спасибо!
1 ответ:
После некоторого измельчения и обсуждения с хорошим другом , мы выяснили, что прокладка, вставленная функцией loadPBM, для выравнивания памяти, это раздел:
aux=image->cols & 0x3; if (aux!=0){ image->widthStep+=4-aux; }
Вызывал запись байтов в каждом элементе матрицы, чтобы перейти к следующему элементу, для изображений, ширина которых не кратна 4, искажая их, из-за изменения их шага ширины. Это объясняет, почему алгоритм работал для некоторых неквадратичных матриц, но не для всех. Для тех, кто видел пост и также задавались вопросом, почему это происходит, надеюсь, это поможет! Вы можете просто удалить эту добавленную обивку, и она будет работать как заклинание