В вашей свертке есть две неверные вещи, которые, вероятно, не вызывают сбой. Во-первых, это стиль: вы используете x
для перебора строк изображения, что я представляю больше как смещение y
, и наоборот. Во-вторых, когда вы вычисляете сумму, вы не сбрасываете переменную sum = 0
до оценки ядра (двух внутренних циклов) для каждого пикселя. Вместо этого вы накапливаете sum
по всем пикселям, что, вероятно, в конечном итоге приводит к целочисленному переполнению. Хотя, строго говоря, это UB и может привести к сбою, это не та проблема, с которой вы столкнулись.
Если вы любезно подтвердите, что сбой происходит на первом пикселе (x = ksize/2
, y = ksize/2
), то, поскольку сбой происходит на первом коэффициенте, считанном из ядра, я подозреваю, что вы могли передать «не ту вещь» как kernel
. В представленном виде kernel
является int**
. Для размера ядра 3x3 это означает, что для корректного вызова этой функции вы должны были выделить в куче или стеке массив int*
, где вы хранили 3 указателя на int
массивов по 3 коэффициента в каждом. Если вместо этого вы передали массив int[3][3]
, функция свертки попытается интерпретировать первые один или два int
в массиве как указатель на int
, когда это не так, и попытается разыменовать его, чтобы получить коэффициент. Это, скорее всего, вызовет segfault.
Я также не знаю, почему вы возвращаете накопленную сумму. Это не «традиционный» вывод свертки, но я предполагаю, что вас интересовала средняя яркость выходного изображения, что является законным; В этом случае вы должны использовать отдельный и более широкий целочисленный аккумулятор (long
или long long
) и в конце разделить его на количество пикселей в выходе.
Вероятно, вы нашли структуру данных PGM в Интернете, скажем, здесь. Позвольте мне расстаться с этим практическим советом. В моей области (компьютерное зрение) выбранная библиотека компьютерного зрения OpenCV не выражает матрицу в виде массива row
указателей на буферы из col
элементов. Вместо этого выделяется большой блок памяти, в данном случае размером не менее image->row * image->col * sizeof(int)
, но часто image->row * image->step * sizeof(int)
, где image->step
равно image->col
, округленному до следующего числа, кратного 4 или 16. Затем сохраняется только один указатель, указатель на основание всего изображения, хотя необходимо сохранить дополнительное поле (шаг), если изображения не являются непрерывными.
Поэтому я бы переработал ваш код следующим образом:
/* Includes */
#include <stdlib.h>
/* Defines */
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
/* Structure */
/**
* Mat structure.
*
* Stores the number of rows and columns in the matrix, the step size
* (number of elements to jump from one row to the next; must be larger than or
* equal to the number of columns), and a pointer to the first element.
*/
typedef struct Mat{
int rows;
int cols;
int step;
int* data;
} Mat;
/* Functions */
/**
* Allocation. Allocates a matrix big enough to hold rows * cols elements.
*
* If a custom step size is wanted, it can be given. Otherwise, an invalid one
* can be given (such as 0 or -1), and the step size will be chosen
* automatically.
*
* If a pointer to existing data is provided, don't bother allocating fresh
* memory. However, in that case, rows, cols and step must all be provided and
* must be correct.
*
* @param [in] rows The number of rows of the new Mat.
* @param [in] cols The number of columns of the new Mat.
* @param [in] step The step size of the new Mat. For newly-allocated
* images (existingData == NULL), can be <= 0, in
* which case a default step size is chosen; For
* pre-existing data (existingData != NULL), must be
* provided.
* @param [in] existingData A pointer to existing data. If NULL, a fresh buffer
* is allocated; Otherwise the given data is used as
* the base pointer.
* @return An allocated Mat structure.
*/
Mat allocMat(int rows, int cols, int step, int* existingData){
Mat M;
M.rows = max(rows, 0);
M.cols = max(cols, 0);
M.step = max(step, M.cols);
if(rows <= 0 || cols <= 0){
M.data = 0;
}else if(existingData == 0){
M.data = malloc(M.rows * M.step * sizeof(*M.data));
}else{
M.data = existingData;
}
return M;
}
/**
* Convolution. Convolves input by the given kernel (centered) and stores
* to output. Does not handle boundaries (i.e., in locations near the border,
* leaves output unchanged).
*
* @param [in] input The input image.
* @param [in] kern The kernel. Both width and height must be odd.
* @param [out] output The output image.
* @return Average brightness of output.
*
* Note: None of the image buffers may overlap with each other.
*/
int convolution(const Mat* input, const Mat* kern, Mat* output){
int i, j, x, y;
int coeff, data;
int sum;
int avg;
long long acc = 0;
/* Short forms of the image dimensions */
const int iw = input ->cols, ih = input ->rows, is = input ->step;
const int kw = kern ->cols, kh = kern ->rows, ks = kern ->step;
const int ow = output->cols, oh = output->rows, os = output->step;
/* Kernel half-sizes and number of elements */
const int kw2 = kw/2, kh2 = kh/2;
const int kelem = kw*kh;
/* Left, right, top and bottom limits */
const int l = kw2,
r = max(min(iw-kw2, ow-kw2), l),
t = kh2,
b = max(min(ih-kh2, oh-kh2), t);
/* Total number of pixels */
const int totalPixels = (r-l)*(b-t);
/* Input, kernel and output base pointers */
const int* iPtr = input ->data;
const int* kPtr = kern ->data + kw2 + ks*kh2;
int* oPtr = output->data;
/* Iterate over pixels of image */
for(y=t; y<b; y++){
for(x=l; x<r; x++){
sum = 0;
/* Iterate over elements of kernel */
for(i=-kh2; i<=kh2; i++){
for(j=-kw2; j<=kw2; j++){
data = iPtr[j + is*i + x];
coeff = kPtr[j + ks*i ];
sum += data * coeff;
}
}
/* Compute average. Add to accumulator and store as output. */
avg = sum / kelem;
acc += avg;
oPtr[x] = avg;
}
/* Bump pointers by one row step. */
iPtr += is;
oPtr += os;
}
/* Compute average brightness over entire output */
if(totalPixels == 0){
avg = 0;
}else{
avg = acc/totalPixels;
}
/* Return average brightness */
return avg;
}
/**
* Main
*/
int main(int argc, char* argv[]){
/**
* Coefficients of K. Binomial 3x3, separable. Unnormalized (weight = 16).
* Step = 3.
*/
int Kcoeff[3][3] = {{1, 2, 1}, {2, 4, 2}, {1, 2, 1}};
Mat I = allocMat(1920, 1080, 0, 0);/* FullHD 1080p: 1920x1080 */
Mat O = allocMat(1920, 1080, 0, 0);/* FullHD 1080p: 1920x1080 */
Mat K = allocMat( 3, 3, 3, &Kcoeff[0][0]);
/* Fill Mat I with something.... */
/* Convolve with K... */
int avg = convolution(&I, &K, &O);
/* Do something with O... */
/* Return */
return 0;
}
Справка: Многолетний опыт работы с компьютерным зрением.
person
Iwillnotexist Idonotexist
schedule
17.10.2013
convolution()
? - person Iwillnotexist Idonotexist   schedule 18.10.2013