Сообщение Re[5]: 2D-Linq и оптимизация цифровых фильтров от 29.06.2018 4:04
Изменено 29.06.2018 4:20 Pavel Dvorkin
Re[5]: 2D-Linq и оптимизация цифровых фильтров
Здравствуйте, Sinclair, Вы писали:
S>Сходу тяжко врубиться в 20 страниц научного текста. Как выглядит код бинаризации по Sauvola? Какая часть алгоритма является "переменной" (зависит от специфики конкретной задачи), а какая — общей?
Держи
Сама идея достаточно проста — вычисляются суммы и суммы квадратов для каждого пикселя , считая его центром окна размером в w (обычно 15-25), потом простенькая формула
Проблема в том, что если это делать в лоб, то будет O(N*M*w^2)
Поэтому вначале строятся массивы, содержащие суммы и суммы квадратов для каждого пикселя , и строятся они рекуррентно. Я пометил этот кусок // START и // END.
PD>>Нахождение линий по Хафу
PD>>https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A5%D0%B0%D1%84%D0%B0
S>Опять же — интересно было бы посмотреть на то, как выглядит типичный код на С++, который ищет какие-то примитивы при помощи преобразования Хафа или
Мы его сами не писали, но вот тебе код, и даже на C#
PD>>Все это 99%, а простенький фильтр — 1%. Это тоже предложишь на LinQ переводить ? А если нет — зачем мне 1% кода переводить на LinQ ?
S>Нет. Я предложу писать код в максимально читаемой форме, где сказано "что", но не сказано "как".
Ну напишем 1% кода "в максимально читаемой форме", а 99% останутся в нечитаемой.
PD>>Эээ, не передергивай. Он как раз потратил время на оптимизацию, потому что в алгоритмах обработки изображений O(N^2) — это очень хорошо, а зачастую бывает существенно хуже. А ты этой оптимизацией и не занимался, да и не ясно, сможешь ли вообще ей заняться.
S>Я-то как раз смогу. Причём эта оптимизация пригодится не только в этой строчке этой программы, а во всех местах, где у меня вычисляется фильтр по изображению.
PD>>Ты сумел показать, что и этот фильтр можно подвести под механизм последовательной обработки, пусть понимаемой несколько расширенно. Однако когда дело дойдет до тех алгоритмов, где обработка ведется не последовательным, а более хитрым способом, ничего путного у тебя не получится
S>Ждём пример такого алгоритма. Как обычно — интересует алгоритм, в котором есть "переменная часть". В случае с фильтром у нас было задание ядра; в случае с СУБД — задание предиката для поиска.
Ну вот тебе Савола. В общем-то, тот же фильтр , так что бери его в качестве переменной части. Правда, окно там не размера 2, а обычно 15-25.
Ну и для Хафа тоже есть переменная часть. Например, можно окружности искать
PD>>Это как с последовательным доступом к файлам vs. прямым доступом. Если надо писать или читать в/из поток(а) — классы последовательного доступа замечательно работают. Если же надо файл модифицировать сложным образом — применять классы последовательного доступа, наверное, все же можно , но лучше не стоит.
S>Вот как раз "императивный for" — это алгоритм последовательного доступа.
Вообще-то, кроме for, есть еще и while
S>Сходу тяжко врубиться в 20 страниц научного текста. Как выглядит код бинаризации по Sauvola? Какая часть алгоритма является "переменной" (зависит от специфики конкретной задачи), а какая — общей?
Держи
void binarize(int image_width, int image_height, unsigned char *bin_image, unsigned char *gray_image,
int w, double k){
int whalf = w>>1;
// Calculate the integral image, and integral of the squared image
double **integral_image,**rowsum_image;
double **integral_sqimg,**rowsum_sqimg;
unsigned char** gray_image_ptr = new unsigned char*[image_height];
gray_image_ptr[0] = gray_image;
for(int i = 1; i < image_height; i++)
gray_image_ptr[i] = gray_image_ptr[i-1] + image_width;
unsigned char** bin_image_ptr = new unsigned char*[image_height];
bin_image_ptr[0] = bin_image;
for(int i = 1; i < image_height; i++)
bin_image_ptr[i] = bin_image_ptr[i-1] + image_width;
integral_image = new double*[image_width];
for(int i = 0; i < image_width; i++)
integral_image[i] = new double[image_height];
rowsum_image = new double*[image_width];
for(int i = 0; i < image_width; i++)
rowsum_image[i] = new double[image_height];
integral_sqimg = new double*[image_width];
for(int i = 0; i < image_width; i++)
integral_sqimg[i] = new double[image_height];
rowsum_sqimg = new double*[image_width];
for(int i = 0; i < image_width; i++)
rowsum_sqimg[i] = new double[image_height];
int xmin,ymin,xmax,ymax;
double diagsum,idiagsum,diff,sqdiagsum,sqidiagsum,sqdiff,area;
double mean,std,threshold;
// START
for(int j=0; j<image_height; j++){
rowsum_image[0][j] = gray_image_ptr[j][0];
rowsum_sqimg[0][j] = gray_image_ptr[j][0]*gray_image_ptr[j][0];
}
for(int i=1; i<image_width; i++){
for(int j=0; j<image_height; j++){
rowsum_image[i][j] = rowsum_image[i-1][j] + gray_image_ptr[j][i];
rowsum_sqimg[i][j] = rowsum_sqimg[i-1][j] + gray_image_ptr[j][i]*gray_image_ptr[j][i];
}
}
for(int i=0; i<image_width; i++){
integral_image[i][0] = rowsum_image[i][0];
integral_sqimg[i][0] = rowsum_sqimg[i][0];
}
for(int i=0; i<image_width; i++){
for(int j=1; j<image_height; j++){
integral_image[i][j] = integral_image[i][j-1] + rowsum_image[i][j];
integral_sqimg[i][j] = integral_sqimg[i][j-1] + rowsum_sqimg[i][j];
}
}
// END
//Calculate the mean and standard deviation using the integral image
for(int i=0; i<image_width; i++){
for(int j=0; j<image_height; j++){
xmin = __max(0,i-whalf);
ymin = __max(0,j-whalf);
xmax = __min(image_width-1,i+whalf);
ymax = __min(image_height-1,j+whalf);
area = (xmax-xmin+1)*(ymax-ymin+1);
// area can't be 0 here
// proof (assuming whalf >= 0):
// we'll prove that (xmax-xmin+1) > 0,
// (ymax-ymin+1) is analogous
// It's the same as to prove: xmax >= xmin
// image_width - 1 >= 0 since image_width > i >= 0
// i + whalf >= 0 since i >= 0, whalf >= 0
// i + whalf >= i - whalf since whalf >= 0
// image_width - 1 >= i - whalf since image_width > i
// --IM
// ASSERT(area);
if(!xmin && !ymin){ // Point at origin
diff = integral_image[xmax][ymax];
sqdiff = integral_sqimg[xmax][ymax];
}
else if(!xmin && ymin){ // first column
diff = integral_image[xmax][ymax] - integral_image[xmax][ymin-1];
sqdiff = integral_sqimg[xmax][ymax] - integral_sqimg[xmax][ymin-1];
}
else if(xmin && !ymin){ // first row
diff = integral_image[xmax][ymax] - integral_image[xmin-1][ymax];
sqdiff = integral_sqimg[xmax][ymax] - integral_sqimg[xmin-1][ymax];
}
else{ // rest of the image
diagsum = integral_image[xmax][ymax] + integral_image[xmin-1][ymin-1];
idiagsum = integral_image[xmax][ymin-1] + integral_image[xmin-1][ymax];
diff = diagsum - idiagsum;
sqdiagsum = integral_sqimg[xmax][ymax] + integral_sqimg[xmin-1][ymin-1];
sqidiagsum = integral_sqimg[xmax][ymin-1] + integral_sqimg[xmin-1][ymax];
sqdiff = sqdiagsum - sqidiagsum;
}
mean = diff/area;
std = sqrt((sqdiff - diff*diff/area)/(area-1));
threshold = mean*(1+k*((std/128)-1));
if(gray_image_ptr[j][i] <= threshold)
bin_image_ptr[j][i] = 0;
else
bin_image_ptr[j][i] = 255;
}
}
delete[] gray_image_ptr;
delete[] bin_image_ptr;
for(int i = 0; i < image_width; i++)
delete[] integral_image[i];
delete[] integral_image;
for(int i = 0; i < image_width; i++)
delete[] rowsum_image[i];
delete[] rowsum_image;
for(int i = 0; i < image_width; i++)
delete[] integral_sqimg[i];
delete[] integral_sqimg;
for(int i = 0; i < image_width; i++)
delete[] rowsum_sqimg[i];
delete []rowsum_sqimg;
}
Сама идея достаточно проста — вычисляются суммы и суммы квадратов для каждого пикселя , считая его центром окна размером в w (обычно 15-25), потом простенькая формула
Проблема в том, что если это делать в лоб, то будет O(N*M*w^2)
Поэтому вначале строятся массивы, содержащие суммы и суммы квадратов для каждого пикселя , и строятся они рекуррентно. Я пометил этот кусок // START и // END.
PD>>Нахождение линий по Хафу
PD>>https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A5%D0%B0%D1%84%D0%B0
S>Опять же — интересно было бы посмотреть на то, как выглядит типичный код на С++, который ищет какие-то примитивы при помощи преобразования Хафа или
Мы его сами не писали, но вот тебе код, и даже на C#
PD>>Все это 99%, а простенький фильтр — 1%. Это тоже предложишь на LinQ переводить ? А если нет — зачем мне 1% кода переводить на LinQ ?
S>Нет. Я предложу писать код в максимально читаемой форме, где сказано "что", но не сказано "как".
Ну напишем 1% кода "в максимально читаемой форме", а 99% останутся в нечитаемой.
PD>>Эээ, не передергивай. Он как раз потратил время на оптимизацию, потому что в алгоритмах обработки изображений O(N^2) — это очень хорошо, а зачастую бывает существенно хуже. А ты этой оптимизацией и не занимался, да и не ясно, сможешь ли вообще ей заняться.
S>Я-то как раз смогу. Причём эта оптимизация пригодится не только в этой строчке этой программы, а во всех местах, где у меня вычисляется фильтр по изображению.
PD>>Ты сумел показать, что и этот фильтр можно подвести под механизм последовательной обработки, пусть понимаемой несколько расширенно. Однако когда дело дойдет до тех алгоритмов, где обработка ведется не последовательным, а более хитрым способом, ничего путного у тебя не получится
S>Ждём пример такого алгоритма. Как обычно — интересует алгоритм, в котором есть "переменная часть". В случае с фильтром у нас было задание ядра; в случае с СУБД — задание предиката для поиска.
Ну вот тебе Савола. В общем-то, тот же фильтр , так что бери его в качестве переменной части. Правда, окно там не размера 2, а обычно 15-25.
Ну и для Хафа тоже есть переменная часть. Например, можно окружности искать
PD>>Это как с последовательным доступом к файлам vs. прямым доступом. Если надо писать или читать в/из поток(а) — классы последовательного доступа замечательно работают. Если же надо файл модифицировать сложным образом — применять классы последовательного доступа, наверное, все же можно , но лучше не стоит.
S>Вот как раз "императивный for" — это алгоритм последовательного доступа.
Вообще-то, кроме for, есть еще и while
Re[5]: 2D-Linq и оптимизация цифровых фильтров
Здравствуйте, Sinclair, Вы писали:
S>Сходу тяжко врубиться в 20 страниц научного текста. Как выглядит код бинаризации по Sauvola? Какая часть алгоритма является "переменной" (зависит от специфики конкретной задачи), а какая — общей?
Держи
Сама идея достаточно проста — вычисляются суммы и суммы квадратов для каждого пикселя , считая его центром окна размером в w (обычно 15-25), потом простенькая формула
Проблема в том, что если это делать в лоб, то будет O(N*M*w^2)
Поэтому вначале строятся массивы, содержащие суммы и суммы квадратов для каждого пикселя , и строятся они рекуррентно. Я пометил этот кусок // START и // END.
PD>>Нахождение линий по Хафу
PD>>https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A5%D0%B0%D1%84%D0%B0
S>Опять же — интересно было бы посмотреть на то, как выглядит типичный код на С++, который ищет какие-то примитивы при помощи преобразования Хафа или
Мы его сами не писали, но вот тебе код, и даже на C#
PD>>Все это 99%, а простенький фильтр — 1%. Это тоже предложишь на LinQ переводить ? А если нет — зачем мне 1% кода переводить на LinQ ?
S>Нет. Я предложу писать код в максимально читаемой форме, где сказано "что", но не сказано "как".
Ну напишем 1% кода "в максимально читаемой форме", а 99% останутся в нечитаемой.
PD>>Эээ, не передергивай. Он как раз потратил время на оптимизацию, потому что в алгоритмах обработки изображений O(N^2) — это очень хорошо, а зачастую бывает существенно хуже. А ты этой оптимизацией и не занимался, да и не ясно, сможешь ли вообще ей заняться.
S>Я-то как раз смогу. Причём эта оптимизация пригодится не только в этой строчке этой программы, а во всех местах, где у меня вычисляется фильтр по изображению.
PD>>Ты сумел показать, что и этот фильтр можно подвести под механизм последовательной обработки, пусть понимаемой несколько расширенно. Однако когда дело дойдет до тех алгоритмов, где обработка ведется не последовательным, а более хитрым способом, ничего путного у тебя не получится
S>Ждём пример такого алгоритма. Как обычно — интересует алгоритм, в котором есть "переменная часть". В случае с фильтром у нас было задание ядра; в случае с СУБД — задание предиката для поиска.
Ну вот тебе Савола. В общем-то, тот же фильтр , так что бери его в качестве переменной части. Правда, окно там не размера 2, а обычно 15-25.
Ну и для Хафа тоже есть переменная часть. Например, можно окружности искать
PD>>Это как с последовательным доступом к файлам vs. прямым доступом. Если надо писать или читать в/из поток(а) — классы последовательного доступа замечательно работают. Если же надо файл модифицировать сложным образом — применять классы последовательного доступа, наверное, все же можно , но лучше не стоит.
S>Вот как раз "императивный for" — это алгоритм последовательного доступа.
Вообще-то, кроме for, есть еще и while
Кстати, как насчет binary search с помощью LinQ ? Разумеется, без написания цикла.
S>Сходу тяжко врубиться в 20 страниц научного текста. Как выглядит код бинаризации по Sauvola? Какая часть алгоритма является "переменной" (зависит от специфики конкретной задачи), а какая — общей?
Держи
void binarize(int image_width, int image_height, unsigned char *bin_image, unsigned char *gray_image,
int w, double k){
int whalf = w>>1;
// Calculate the integral image, and integral of the squared image
double **integral_image,**rowsum_image;
double **integral_sqimg,**rowsum_sqimg;
unsigned char** gray_image_ptr = new unsigned char*[image_height];
gray_image_ptr[0] = gray_image;
for(int i = 1; i < image_height; i++)
gray_image_ptr[i] = gray_image_ptr[i-1] + image_width;
unsigned char** bin_image_ptr = new unsigned char*[image_height];
bin_image_ptr[0] = bin_image;
for(int i = 1; i < image_height; i++)
bin_image_ptr[i] = bin_image_ptr[i-1] + image_width;
integral_image = new double*[image_width];
for(int i = 0; i < image_width; i++)
integral_image[i] = new double[image_height];
rowsum_image = new double*[image_width];
for(int i = 0; i < image_width; i++)
rowsum_image[i] = new double[image_height];
integral_sqimg = new double*[image_width];
for(int i = 0; i < image_width; i++)
integral_sqimg[i] = new double[image_height];
rowsum_sqimg = new double*[image_width];
for(int i = 0; i < image_width; i++)
rowsum_sqimg[i] = new double[image_height];
int xmin,ymin,xmax,ymax;
double diagsum,idiagsum,diff,sqdiagsum,sqidiagsum,sqdiff,area;
double mean,std,threshold;
// START
for(int j=0; j<image_height; j++){
rowsum_image[0][j] = gray_image_ptr[j][0];
rowsum_sqimg[0][j] = gray_image_ptr[j][0]*gray_image_ptr[j][0];
}
for(int i=1; i<image_width; i++){
for(int j=0; j<image_height; j++){
rowsum_image[i][j] = rowsum_image[i-1][j] + gray_image_ptr[j][i];
rowsum_sqimg[i][j] = rowsum_sqimg[i-1][j] + gray_image_ptr[j][i]*gray_image_ptr[j][i];
}
}
for(int i=0; i<image_width; i++){
integral_image[i][0] = rowsum_image[i][0];
integral_sqimg[i][0] = rowsum_sqimg[i][0];
}
for(int i=0; i<image_width; i++){
for(int j=1; j<image_height; j++){
integral_image[i][j] = integral_image[i][j-1] + rowsum_image[i][j];
integral_sqimg[i][j] = integral_sqimg[i][j-1] + rowsum_sqimg[i][j];
}
}
// END
//Calculate the mean and standard deviation using the integral image
for(int i=0; i<image_width; i++){
for(int j=0; j<image_height; j++){
xmin = __max(0,i-whalf);
ymin = __max(0,j-whalf);
xmax = __min(image_width-1,i+whalf);
ymax = __min(image_height-1,j+whalf);
area = (xmax-xmin+1)*(ymax-ymin+1);
// area can't be 0 here
// proof (assuming whalf >= 0):
// we'll prove that (xmax-xmin+1) > 0,
// (ymax-ymin+1) is analogous
// It's the same as to prove: xmax >= xmin
// image_width - 1 >= 0 since image_width > i >= 0
// i + whalf >= 0 since i >= 0, whalf >= 0
// i + whalf >= i - whalf since whalf >= 0
// image_width - 1 >= i - whalf since image_width > i
// --IM
// ASSERT(area);
if(!xmin && !ymin){ // Point at origin
diff = integral_image[xmax][ymax];
sqdiff = integral_sqimg[xmax][ymax];
}
else if(!xmin && ymin){ // first column
diff = integral_image[xmax][ymax] - integral_image[xmax][ymin-1];
sqdiff = integral_sqimg[xmax][ymax] - integral_sqimg[xmax][ymin-1];
}
else if(xmin && !ymin){ // first row
diff = integral_image[xmax][ymax] - integral_image[xmin-1][ymax];
sqdiff = integral_sqimg[xmax][ymax] - integral_sqimg[xmin-1][ymax];
}
else{ // rest of the image
diagsum = integral_image[xmax][ymax] + integral_image[xmin-1][ymin-1];
idiagsum = integral_image[xmax][ymin-1] + integral_image[xmin-1][ymax];
diff = diagsum - idiagsum;
sqdiagsum = integral_sqimg[xmax][ymax] + integral_sqimg[xmin-1][ymin-1];
sqidiagsum = integral_sqimg[xmax][ymin-1] + integral_sqimg[xmin-1][ymax];
sqdiff = sqdiagsum - sqidiagsum;
}
mean = diff/area;
std = sqrt((sqdiff - diff*diff/area)/(area-1));
threshold = mean*(1+k*((std/128)-1));
if(gray_image_ptr[j][i] <= threshold)
bin_image_ptr[j][i] = 0;
else
bin_image_ptr[j][i] = 255;
}
}
delete[] gray_image_ptr;
delete[] bin_image_ptr;
for(int i = 0; i < image_width; i++)
delete[] integral_image[i];
delete[] integral_image;
for(int i = 0; i < image_width; i++)
delete[] rowsum_image[i];
delete[] rowsum_image;
for(int i = 0; i < image_width; i++)
delete[] integral_sqimg[i];
delete[] integral_sqimg;
for(int i = 0; i < image_width; i++)
delete[] rowsum_sqimg[i];
delete []rowsum_sqimg;
}
Сама идея достаточно проста — вычисляются суммы и суммы квадратов для каждого пикселя , считая его центром окна размером в w (обычно 15-25), потом простенькая формула
Проблема в том, что если это делать в лоб, то будет O(N*M*w^2)
Поэтому вначале строятся массивы, содержащие суммы и суммы квадратов для каждого пикселя , и строятся они рекуррентно. Я пометил этот кусок // START и // END.
PD>>Нахождение линий по Хафу
PD>>https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A5%D0%B0%D1%84%D0%B0
S>Опять же — интересно было бы посмотреть на то, как выглядит типичный код на С++, который ищет какие-то примитивы при помощи преобразования Хафа или
Мы его сами не писали, но вот тебе код, и даже на C#
PD>>Все это 99%, а простенький фильтр — 1%. Это тоже предложишь на LinQ переводить ? А если нет — зачем мне 1% кода переводить на LinQ ?
S>Нет. Я предложу писать код в максимально читаемой форме, где сказано "что", но не сказано "как".
Ну напишем 1% кода "в максимально читаемой форме", а 99% останутся в нечитаемой.
PD>>Эээ, не передергивай. Он как раз потратил время на оптимизацию, потому что в алгоритмах обработки изображений O(N^2) — это очень хорошо, а зачастую бывает существенно хуже. А ты этой оптимизацией и не занимался, да и не ясно, сможешь ли вообще ей заняться.
S>Я-то как раз смогу. Причём эта оптимизация пригодится не только в этой строчке этой программы, а во всех местах, где у меня вычисляется фильтр по изображению.
PD>>Ты сумел показать, что и этот фильтр можно подвести под механизм последовательной обработки, пусть понимаемой несколько расширенно. Однако когда дело дойдет до тех алгоритмов, где обработка ведется не последовательным, а более хитрым способом, ничего путного у тебя не получится
S>Ждём пример такого алгоритма. Как обычно — интересует алгоритм, в котором есть "переменная часть". В случае с фильтром у нас было задание ядра; в случае с СУБД — задание предиката для поиска.
Ну вот тебе Савола. В общем-то, тот же фильтр , так что бери его в качестве переменной части. Правда, окно там не размера 2, а обычно 15-25.
Ну и для Хафа тоже есть переменная часть. Например, можно окружности искать
PD>>Это как с последовательным доступом к файлам vs. прямым доступом. Если надо писать или читать в/из поток(а) — классы последовательного доступа замечательно работают. Если же надо файл модифицировать сложным образом — применять классы последовательного доступа, наверное, все же можно , но лучше не стоит.
S>Вот как раз "императивный for" — это алгоритм последовательного доступа.
Вообще-то, кроме for, есть еще и while
Кстати, как насчет binary search с помощью LinQ ? Разумеется, без написания цикла.