Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

- add Point::ab

- implement Local::ridge, Lindeberg's ridge detector
  • Loading branch information...
commit e5014009fd53fd9de2b45abdeb5f38da66710bde 1 parent ed145f2
dk authored
244 Local/Local.c
@@ -1324,13 +1324,16 @@ PImage IPA__Local_hysteresis(PImage img,HV *profile)
1324 1324 }
1325 1325
1326 1326 static PImage
1327   -gaussian( const char * method, int size, double sigma)
  1327 +gaussian( const char * method, int size, double sigma, Bool laplacian, int mx, int my)
1328 1328 {
1329 1329 register int j, k;
1330   - double x, y, *e=0;
  1330 + double x, *e=0;
1331 1331 int ls,s=size/2;
1332 1332 PImage out;
1333   - Byte *dst;
  1333 + double *dst;
  1334 + Byte * data;
  1335 + double sigma2 = sigma * sigma;
  1336 + double sigma4 = - sigma2 * sigma2;
1334 1337 /* check parameters */
1335 1338 if (size < 2 || size % 2 == 0)
1336 1339 croak("%s: size of gaussian must be an odd number greater than two", method);
@@ -1339,30 +1342,41 @@ gaussian( const char * method, int size, double sigma)
1339 1342 if (!(e = (double *)malloc(((size/2)+1)*sizeof(double)))) {
1340 1343 croak("%s: not enough memory\n", method);
1341 1344 }
1342   - out = createImage( size, size, imByte);
1343   - dst = out-> data;
1344   - ls = out-> lineSize;
  1345 + out = createImage( size, size, imDouble);
  1346 + data = out-> data;
  1347 + ls = out-> lineSize / sizeof(double);
1345 1348 sigma = 2*sigma*sigma;
1346 1349
  1350 +
1347 1351 /* store one dimensional components */
1348   - for (k=0; k<size/2; k++) {
  1352 + for (k=0; k<=size/2; k++) {
1349 1353 x = ((double)(k-s)*(double)(k-s))/sigma;
1350 1354 e[k] = exp(-x);
1351 1355 }
1352   - /* symmetric quadrants */
1353   - for (j=0; j<s; j++) {
1354   - for (k=0; k<s; k++) {
1355   - dst[j*ls+k] =
1356   - dst[j*ls+size-k-1] =
1357   - dst[(size-j-1)*ls+k] =
1358   - dst[(size-j-1)*ls+size-k-1] =
1359   - e[k]*e[j]*255+.5;
1360   - }
  1356 +
  1357 + dst = (double*)data;
  1358 + for (j=0;j<size;j++, data += out->lineSize, dst = (double*)data){
  1359 + for (k=0;k<size;k++){
  1360 + double y = s - j, x = s - k;
  1361 + int ay = (j < s) ? j : 2 * s - j, ax = (k < s) ? k : 2 * s - k;
  1362 + *(dst++)=
  1363 + ( laplacian ? ((x*x/16 + y*y - sigma2)/sigma4) : 1) * e[ax*mx] * e[ay*my];
  1364 + }
1361 1365 }
1362   - /* zero coordinates */
1363   - for (j=0; j<size; j++) {
1364   - y = ((double)(j-s)*(double)(j-s))/sigma;
1365   - dst[j*ls+s] = dst[((int)s)*ls+j]=exp(-y)*255+.5;
  1366 +
  1367 + if ( laplacian) {
  1368 + /* normalize to sum = 0 */
  1369 + double sum = out-> self-> get_stats(( Handle)out, isSum);
  1370 + out-> statsCache = 0;
  1371 + if ( sum != 0) {
  1372 + double * s = ( double*)(out-> data),
  1373 + sub = sum / (out->w * out-> h);
  1374 + int sz = out-> dataSize / sizeof(double);
  1375 + while ( sz--) {
  1376 + *s = *s - sub;
  1377 + s++;
  1378 + }
  1379 + }
1366 1380 }
1367 1381 free(e);
1368 1382 return out;
@@ -1370,7 +1384,12 @@ gaussian( const char * method, int size, double sigma)
1370 1384
1371 1385 PImage IPA__Local_gaussian( int size, double sigma)
1372 1386 {
1373   - return gaussian( "IPA::Local::gaussian", size, sigma);
  1387 + return gaussian( "IPA::Local::gaussian", size, sigma, 0, 1, 1);
  1388 +}
  1389 +
  1390 +PImage IPA__Local_laplacian( int size, double sigma)
  1391 +{
  1392 + return gaussian( "IPA::Local::laplacian", size, sigma, 1, 1, 1);
1374 1393 }
1375 1394
1376 1395 static PImage
@@ -1379,43 +1398,48 @@ convolution( const char * method, PImage in, PImage kernel_img)
1379 1398 register int j, k, l, m, n;
1380 1399 double sum, ksum;
1381 1400 int size = kernel_img-> w;
1382   - int kls = kernel_img-> lineSize - size;
  1401 + int kls;
1383 1402 int dls, sls, marg = size/2;
1384   - Byte * dst, *src, *kernel;
  1403 + double * dst, *src, *kernel, kill_kernel = 0, kill_img = 0;
1385 1404 PImage out;
1386 1405
1387   - if (( kernel_img-> type & imBPP) != 8)
1388   - croak("%s: kernel is not 8-bit image (%x)", method, kernel_img-> type & imBPP);
  1406 + if ( kernel_img-> type != imDouble) {
  1407 + kernel_img = (PImage)kernel_img-> self-> dup(( Handle) kernel_img);
  1408 + kernel_img-> self-> set_type(( Handle) kernel_img, imDouble);
  1409 + kill_kernel = 1;
  1410 + }
  1411 + if ( in-> type != imDouble) {
  1412 + in = (PImage)in-> self-> dup(( Handle) in);
  1413 + in-> self-> set_type(( Handle) in, imDouble);
  1414 + kill_img = 1;
  1415 + }
1389 1416 if ( kernel_img-> w != kernel_img-> h)
1390 1417 croak("%s: kernel sides must be equal", method);
1391   - kernel = kernel_img-> data;
  1418 + kernel = ( double*)kernel_img-> data;
1392 1419 if ( size % 2 == 0)
1393 1420 croak("%s: kernel size (%d) must be odd", method, size);
1394 1421 if ( in-> w < size || in-> h < size)
1395 1422 croak("%s: kernel size (%d) must be smaller than dimensions of image (%d %d)",
1396 1423 method, size, in-> w, in-> h);
  1424 +
1397 1425 out = create_compatible_image(in,false);
1398   - dst = out-> data;
1399   - dls = out-> lineSize;
1400   - src = in-> data;
1401   - sls = in-> lineSize;
  1426 + dst = ( double*)out-> data;
  1427 + dls = out-> lineSize / sizeof(double);
  1428 + src = ( double*)in-> data;
  1429 + sls = in-> lineSize / sizeof(double);
1402 1430 ksum = kernel_img-> self-> get_stats(( Handle) kernel_img, isSum);
  1431 + if ( ksum == 0) ksum = 1;
  1432 + kls = kernel_img-> lineSize / sizeof(double) - size;
  1433 + ksum=1;
1403 1434
1404 1435 for (j=marg; j<in->h-marg; j++) {
1405 1436 for (k=marg; k<in->w-marg; k++) {
1406 1437 for (sum=0.0,n=0,l=0; l<size; l++, n += kls) {
1407 1438 for (m=0; m<size; m++) {
1408   - sum += (double)src[(j-marg+l)*sls+k-marg+m]*(double)kernel[n++];
  1439 + sum += src[(j-marg+l)*sls+k-marg+m]*kernel[n++];
1409 1440 }
1410 1441 }
1411   - sum /= ksum;
1412   - if ( sum < 0.0) {
1413   - dst[j*dls+k] = 0;
1414   - } else if ( sum > 255.0) {
1415   - dst[j*dls+k] = 255;
1416   - } else {
1417   - dst[j*dls+k] = sum + .5;
1418   - }
  1442 + dst[j*dls+k] = sum/ksum;
1419 1443 }
1420 1444 }
1421 1445 /* top and bottom margins */
@@ -1434,6 +1458,8 @@ convolution( const char * method, PImage in, PImage kernel_img)
1434 1458 }
1435 1459 }
1436 1460
  1461 + if ( kill_kernel) Object_destroy((Handle) kernel_img);
  1462 + if ( kill_img) Object_destroy((Handle) in);
1437 1463 return out;
1438 1464 }
1439 1465
@@ -1443,12 +1469,8 @@ IPA__Local_convolution( PImage img, PImage kernel_img)
1443 1469 const char *method="IPA::Local::convolution";
1444 1470 if ( !img || !kind_of(( Handle) img, CImage))
1445 1471 croak("%s: not an image passed", method);
1446   - if ( img-> type != imByte)
1447   - croak("%s: image is not 8-bit grayscale", method);
1448 1472 if ( !kernel_img || !kind_of(( Handle) kernel_img, CImage))
1449 1473 croak("%s: not an image passed", method);
1450   - if ( kernel_img-> type != imByte)
1451   - croak("%s: image is not 8-bit grayscale", method);
1452 1474 return convolution( method, img, kernel_img);
1453 1475 }
1454 1476
@@ -1637,13 +1659,14 @@ canny( const char * method,
1637 1659 int ls, dls;
1638 1660
1639 1661 /* create gaussian smoothing filter */
1640   - g = gaussian( method, size, sigma);
  1662 + g = gaussian( method, size, sigma, 0, 1, 1);
1641 1663 out = create_compatible_image( in, false);
1642 1664 dst = out-> data;
1643 1665 dls = out-> lineSize;
1644 1666
1645 1667 /* convolve with the gaussian */
1646 1668 smoothed = convolution( method, in, g);
  1669 + smoothed-> self-> set_type(( Handle) smoothed, imByte);
1647 1670 Object_destroy(( Handle) g);
1648 1671 /* return smoothed */
1649 1672 /* create the First Order Gaussian filtered image */
@@ -1754,11 +1777,12 @@ scale( const char * method,
1754 1777 PImage g, smoothed;
1755 1778 /* create gaussian smoothing filter */
1756 1779 if ( t < 0) croak("%s: 't' must be positive", method);
1757   - g = gaussian( method, size, sqrt(t));
  1780 + g = gaussian( method, size, sqrt(t), 0, 1, 1);
1758 1781 /* normalize the gaussian */
1759 1782
1760 1783 /* convolve with the gaussian */
1761 1784 smoothed = convolution( method, in, g);
  1785 + smoothed-> self-> set_type(( Handle) smoothed, imByte);
1762 1786 Object_destroy(( Handle) g);
1763 1787 /* return smoothed */
1764 1788 return smoothed;
@@ -1781,6 +1805,51 @@ PImage IPA__Local_scale(PImage img,HV *profile)
1781 1805
1782 1806 return scale(method,img,size,t);
1783 1807 }
  1808 +
  1809 +static PImage
  1810 +d_rotate( PImage in, double alpha)
  1811 +{
  1812 + int x, y, ls = in-> lineSize, ols = in-> lineSize / sizeof(double), nx, ny, sx, sy;
  1813 + double * src, * dst;
  1814 + Byte * bdst;
  1815 + PImage out = create_compatible_image(in,false);
  1816 + double sina = sin( alpha), cosa = cos( alpha);
  1817 +
  1818 + sx = in-> w/2;
  1819 + sy = in-> h/2;
  1820 + bdst = out-> data;
  1821 + dst = ( double*) bdst;
  1822 + src = (double*) in-> data;
  1823 + for ( y = 0; y < in-> h; y++, bdst += ls, dst = (double*) bdst) {
  1824 + for ( x = 0; x < in-> w; x++) {
  1825 + nx = (x-sx) * cosa - (y-sy) * sina + sx;
  1826 + ny = (x-sx) * sina + (y-sy) * cosa + sy;
  1827 + if ( nx >= 0 && nx < in-> w &&
  1828 + ny >= 0 && ny < in-> h)
  1829 + dst[x] = src[ny * ols + nx];
  1830 + }
  1831 + }
  1832 + return out;
  1833 +}
  1834 +
  1835 +static PImage
  1836 +d_rotate90( PImage in)
  1837 +{
  1838 + int x, y, max, ls = in-> lineSize, ols = in-> lineSize / sizeof(double);
  1839 + double * src, * dst;
  1840 + Byte * bdst;
  1841 + PImage out = create_compatible_image(in,false);
  1842 +
  1843 + max = ( in-> h < in-> w) ? in-> h : in-> w;
  1844 + bdst = out-> data;
  1845 + dst = ( double*) bdst;
  1846 + src = (double*) in-> data;
  1847 + for ( y = 0; y < max; y++, bdst += ls, dst = (double*) bdst) {
  1848 + for ( x = 0; x < max; x++)
  1849 + dst[x] = src[y * ols + x];
  1850 + }
  1851 + return out;
  1852 +}
1784 1853
1785 1854 /*
1786 1855 Lindeberg ridge detector
@@ -1788,21 +1857,17 @@ PImage IPA__Local_scale(PImage img,HV *profile)
1788 1857 A= t^(2y) ((Lxx-Lyy)^2 + 4Lxy^2).
1789 1858 Lindeberg 1994.
1790 1859
1791   - t ( scale ) and y ( gamma-normalizer ) can be represented by 'mul'
  1860 + t ( scale ) and y ( gamma-normalizer )
1792 1861 Don't know exactly, but suppose Lxy^2 == Lxy*Lyx
1793 1862 */
1794 1863 PImage IPA__Local_ridge(PImage img,HV *profile)
1795 1864 {
1796   - PImage xx, yy, xy, yx, tmp;
  1865 + PImage xx, yy, xy, yx, lxx, lyy, lxy, lyx, l, tmp;
1797 1866 Bool anorm = false;
1798 1867 const char *method="IPA::Local::ridge";
1799   - double yyf[9] = { 0,0,0,-1,2,-1,0,0,0};
1800   - double xxf[9] = { 0,-1,0,0,2,0,0,-1,0};
1801   - double xyf[9] = { -1,0,0,0,2,0,0,0,-1};
1802   - double yxf[9] = { 0,0,-1,0,2,0,-1,0,0};
1803   - double mul = 1;
1804   - int ls, y, x, yls;
1805   - Long *xxd, *yyd, *xyd, *yxd, *res;
  1868 + double mul = 1, scale = 2, gamma = 1;
  1869 + int ls, y, x, yls, size = 3, msize;
  1870 + double *xxd, *yyd, *xyd, *yxd, *res, texp;
1806 1871
1807 1872 if ( !img || !kind_of(( Handle) img, CImage))
1808 1873 croak("%s: not an image passed", method);
@@ -1812,23 +1877,62 @@ PImage IPA__Local_ridge(PImage img,HV *profile)
1812 1877
1813 1878 if ( pexist(a)) anorm = pget_B(a);
1814 1879 if ( pexist(mul)) mul = pget_f(mul);
1815   -
1816   - /* filter3x3 in rawOutput always returns long pixels - check anyway */
1817   - xx = filter3x3( method, img, xxf, 1, true, true, CONV_TRUNC, 0);
1818   - if ( xx-> type != imLong)
1819   - croak("%s: panic: filter3x3 returned not imLong pixels\n", method);
1820   - yy = filter3x3( method, img, yyf, 1, true, true, CONV_TRUNC, 0);
1821   - xy = filter3x3( method, img, xyf, 1, true, true, CONV_TRUNC, 0);
1822   - yx = filter3x3( method, img, yxf, 1, true, true, CONV_TRUNC, 0);
1823   -
1824   - xxd = ( Long*) xx-> data;
1825   - yyd = ( Long*) yy-> data;
1826   - xyd = ( Long*) xy-> data;
1827   - yxd = ( Long*) yx-> data;
  1880 + if ( pexist(scale)) scale = pget_f(scale);
  1881 + if ( pexist(size))
  1882 + size = pget_i(size);
  1883 + else
  1884 + size = sqrt(scale);
  1885 + if ( size < 3) size = 3;
  1886 + if (( size % 2) == 0) size++;
  1887 + if ( pexist(gamma)) gamma = pget_f(gamma);
  1888 +
  1889 + /* XXX gamma */
  1890 + texp = scale * scale;
  1891 + if ( !anorm) texp *= texp;
  1892 +
  1893 + /* prepare second derivative masks */
  1894 + msize = size * 1.5;
  1895 + if (( msize % 2) == 0) msize++;
  1896 + l = gaussian( method, msize, sqrt(scale), 1, 0, 1);
  1897 + tmp = d_rotate( l, 3.14159/4);
  1898 + lxy = ( PImage) tmp-> self-> extract(( Handle) tmp, tmp-> w - size, tmp-> h - size, size, size);
  1899 + lxx = ( PImage) l-> self-> extract(( Handle) l, l-> w - size, l-> h - size, size, size);
  1900 + lyy = d_rotate90( lxx);
  1901 + Object_destroy(( Handle)tmp);
  1902 + Object_destroy(( Handle)l);
  1903 + /* normalize skewed sums = 0 */
  1904 + {
  1905 + double sum = lxy-> self-> get_stats(( Handle)lxy, isSum);
  1906 + if ( sum != 0) {
  1907 + double * s = ( double*)(lxy-> data),
  1908 + sub = sum / (lxy->w * lxy-> h);
  1909 + int sz = lxy-> dataSize / sizeof(double);
  1910 + while ( sz--) {
  1911 + *s = *s - sub;
  1912 + s++;
  1913 + }
  1914 + }
  1915 + }
  1916 + lyx = d_rotate90( lxy);
  1917 +
  1918 + /* convolute the image with them */
  1919 + xx = convolution( method, img, lxx);
  1920 + Object_destroy(( Handle)lxx);
  1921 + yy = convolution( method, img, lyy);
  1922 + Object_destroy(( Handle)lyy);
  1923 + xy = convolution( method, img, lxy);
  1924 + Object_destroy(( Handle)lxy);
  1925 + yx = convolution( method, img, lyx);
  1926 + Object_destroy(( Handle)lyx);
  1927 +
  1928 + xxd = ( double*) xx-> data;
  1929 + yyd = ( double*) yy-> data;
  1930 + xyd = ( double*) xy-> data;
  1931 + yxd = ( double*) yx-> data;
1828 1932
1829 1933 tmp = create_compatible_image( xx, false);
1830   - res = ( Long*) tmp-> data;
1831   - for ( y = 0, ls = xx-> lineSize / sizeof(Long), yls = 0; y < img-> h; y++, yls += ls) {
  1934 + res = ( double*) tmp-> data;
  1935 + for ( y = 0, ls = xx-> lineSize / sizeof(double), yls = 0; y < img-> h; y++, yls += ls) {
1832 1936 for ( x = 0; x < xx-> w; x++) {
1833 1937 double
1834 1938 Lxx = xxd[ yls + x],
@@ -1837,7 +1941,7 @@ PImage IPA__Local_ridge(PImage img,HV *profile)
1837 1941 Lyx = yxd[ yls + x];
1838 1942 double A = mul * ((Lxx - Lyy) * (Lxx - Lyy) + 4 * Lxy * Lyx);
1839 1943 if ( !anorm) A = ( A * ( Lxx + Lyy) * ( Lxx + Lyy));
1840   - res[ yls + x] = A + .5;
  1944 + res[ yls + x] = A;
1841 1945 }
1842 1946 }
1843 1947 Object_destroy(( Handle)xx);
1  Local/Local.cls
@@ -17,6 +17,7 @@ package IPA::Local {
17 17 PImage unionFind(PImage input,HV *profile);
18 18 PImage hysteresis(PImage input, HV*profile);
19 19 PImage gaussian( int size, double sigma);
  20 + PImage laplacian( int size, double sigma);
20 21 TwoImages gradients(PImage input);
21 22 PImage canny( PImage input, HV*profile);
22 23 PImage nms( PImage input, HV*profile);
1  Local/Local.pm
@@ -26,6 +26,7 @@ $VERSION = '0.01';
26 26 unionFind
27 27 hysteresis
28 28 gaussian
  29 + laplacian
29 30 gradients
30 31 canny
31 32 nms
19 Point/Point.c
@@ -276,8 +276,12 @@ PImage IPA__Point_threshold(PImage img,HV *profile)
276 276
277 277 if (pexist(minvalue)) minvalue=pget_f(minvalue);
278 278 if (pexist(maxvalue)) maxvalue=pget_f(maxvalue);
  279 +#undef true
  280 +#undef false
279 281 if (pexist(true)) val1=pget_f(true);
280 282 if (pexist(false)) val0=pget_f(false);
  283 +#define true 1
  284 +#define false 0
281 285 if (pexist(preserve)) preserve=pget_B(preserve);
282 286
283 287 out = create_compatible_image( img, false);
@@ -596,3 +600,18 @@ IPA__Point_average( SV *list)
596 600
597 601 return oimg;
598 602 }
  603 +
  604 +
  605 +PImage
  606 +IPA__Point_ab( PImage in, double mul, double add)
  607 +{
  608 + const char *method="IPA::Point::ab";
  609 + PImage out;
  610 +
  611 + if ( !in || !kind_of(( Handle) in, CImage))
  612 + croak("%s: not an image passed", method);
  613 +
  614 + out = create_compatible_image( in, false);
  615 + PIX_SRC_DST( in, out, *dst = *src * mul + add);
  616 + return out;
  617 +}
1  Point/Point.cls
@@ -9,5 +9,6 @@ package IPA::Point {
9 9 PImage subtract(PImage input1,PImage input2,HV *profile);
10 10 PImage mask( PImage msk, HV *profile);
11 11 PImage average( SV *list);
  12 + PImage ab( PImage in, double mul, double add);
12 13 }
13 14
2  Point/Point.pm
@@ -9,7 +9,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
9 9 @ISA = qw(Exporter DynaLoader);
10 10 $VERSION = '0.01';
11 11 @EXPORT = qw();
12   -@EXPORT_OK = qw(combine threshold gamma remap subtract mask equalize);
  12 +@EXPORT_OK = qw(combine threshold gamma remap subtract mask equalize ab);
13 13 %EXPORT_TAGS = ();
14 14 sub dl_load_flags { 0x01 };
15 15
4 bin/iterm
@@ -80,7 +80,7 @@ sub invert
80 80 my $img = shift;
81 81 $img = $i unless defined $img;
82 82 show_error('No image'), return unless defined $img;
83   - $img-> resample( 0, 255, 255, 0);
  83 + $img-> resample( $i-> rangeLo, $i-> rangeHi, $i-> rangeHi, $i-> rangeLo);
84 84 }
85 85
86 86 sub stretch
@@ -101,7 +101,7 @@ sub info
101 101 $img-> type & im::BPP,
102 102 (
103 103 (($img-> type & im::GrayScale) ? 'gray ' : '') .
104   - (($img-> type & im::RealNumber) ? 'real ' : '') .
  104 + (($img-> type & im::RealNumber) ? 'float ' : '') .
105 105 (($img-> type & im::ComplexNumber) ? 'complex ' : '') .
106 106 (($img-> type & im::TrigComplexNumber) ? 'trig ' : '')
107 107 )

0 comments on commit e501400

Please sign in to comment.
Something went wrong with that request. Please try again.