├── .gitignore ├── AdaptiveDWMTM ├── AdaptiveDWMTM.pde └── preview.png ├── AdaptiveMMSE ├── AdaptiveMMSE.pde └── preview.png ├── AlphaMeanFilter ├── AlphaMeanFilter.pde └── preview.png ├── Centroid ├── Centroid.pde └── preview.png ├── Circularity ├── Circularity.pde └── preview.png ├── CircularlySymmetricFilter ├── CircularlySymmetricFilter.pde └── preview.png ├── DiscreteCosineTransform ├── DiscreteCosineTransform.pde └── preview.png ├── DiscreteFourierTransform ├── DiscreteFourierTransform.pde └── preview.png ├── GaussianNoise ├── GaussianNoise.pde └── preview.png ├── GeometricMeanFilter ├── GeometricMeanFilter.pde └── preview.png ├── HadamardTransform ├── HadamardTransform.pde └── preview.png ├── HarmonicMeanFilter ├── HarmonicMeanFilter.pde └── preview.png ├── HartleyTransform ├── HartleyTransform.pde └── preview.png ├── HistogramEqualization ├── HistogramEqualization.pde └── preview.png ├── HistogramSpecification ├── HistogramSpecification.pde └── preview.png ├── MaximumAxis ├── MaximumAxis.pde └── preview.png ├── README.md ├── Skeleton ├── Skeleton.pde └── preview.png ├── Thickening ├── Thickening.pde └── preview.png ├── Thinning ├── Thinning.pde └── preview.png ├── TopHatFilter ├── TopHatFilter.pde └── preview.png ├── Warping ├── Warping.pde └── preview.png ├── images ├── boat.png ├── cameraman.png ├── horse.png └── peppers.png └── thumbnails.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | */.DS_Store 3 | **/.DS_Store 4 | 5 | -------------------------------------------------------------------------------- /AdaptiveDWMTM/AdaptiveDWMTM.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Adaptive DW-MTM Filter 9 | // Page 13 10 | 11 | class Image { 12 | int Rows; 13 | int Cols; 14 | char[] Data; 15 | Image(int w, int h){ 16 | Data = new char[w*h]; 17 | Rows = h; 18 | Cols = w; 19 | } 20 | Image(PImage img){ 21 | this(img.width,img.height); 22 | img.loadPixels(); 23 | for (int i = 0; i < img.pixels.length; i++){ 24 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 25 | } 26 | } 27 | PImage toPImage(){ 28 | PImage img = createImage(Cols,Rows,ARGB); 29 | img.loadPixels(); 30 | for (int i = 0; i < img.pixels.length; i++){ 31 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 32 | } 33 | img.updatePixels(); 34 | return img; 35 | } 36 | }; 37 | 38 | void DDMTM_filter(Image IMAGE, Image IMAGE1, float NSTD, float K){ 39 | int X, Y, x1, y1; 40 | int[] med = new int[9]; 41 | int median; 42 | int gray, i, j, temp; 43 | int total, sum, R; 44 | R = IMAGE.Cols; 45 | for (Y = 2; Y < IMAGE.Rows-2; Y++){ 46 | for (X = 2; X < IMAGE.Cols-2; X++){ 47 | total = 0; 48 | for (y1 = -1; y1 <= 1; y1++){ 49 | for (x1 = -1; x1 <= 1; x1++){ 50 | med[total] = (int)IMAGE.Data[X+x1+(Y+y1)*R]; 51 | total++; 52 | } 53 | } 54 | for (j = 1; j <= 8; j++){ 55 | temp = med[j]; 56 | i = j-1; 57 | while (i >= 0 && med[i] > temp){ 58 | med[i+1] = med[i]; 59 | i--; 60 | } 61 | med[i+1] = temp; 62 | } 63 | median=med[4]; 64 | sum = 0; total = 0; 65 | for (y1 = -2; y1 <= 2; y1 ++){ 66 | for (x1 = -2; x1 <= 2; x1 ++){ 67 | gray = (int)IMAGE.Data[X+x1+(Y+y1)*R]; 68 | if (gray>=(median-K*NSTD)){ 69 | if (gray<=(median+K*NSTD)){ 70 | sum+=gray; 71 | total++; 72 | } 73 | } 74 | } 75 | } 76 | IMAGE1.Data[X+Y*R] = (char)((float)sum/(float)total); 77 | } 78 | } 79 | } 80 | 81 | void setup(){ 82 | size(1024,512); 83 | PImage img = loadImage(sketchPath("../images/peppers.png")); 84 | Image IMAGE = new Image(img); 85 | Image IMAGE1 = new Image(img.width,img.height); 86 | float NSTD = 20; 87 | for (int i = 0; i < IMAGE.Data.length; i++){ 88 | IMAGE.Data[i] = (char)constrain(IMAGE.Data[i] + (randomGaussian() * NSTD),0,255); 89 | } 90 | float K = 2; // 1.5 - 2.5 91 | DDMTM_filter(IMAGE,IMAGE1,NSTD,K); 92 | 93 | PImage img0 = IMAGE.toPImage(); 94 | PImage img1 = IMAGE1.toPImage(); 95 | 96 | image(img0,0,0); 97 | image(img1,512,0); 98 | 99 | save("preview.png"); 100 | } 101 | -------------------------------------------------------------------------------- /AdaptiveDWMTM/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/AdaptiveDWMTM/preview.png -------------------------------------------------------------------------------- /AdaptiveMMSE/AdaptiveMMSE.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Adaptive MMSE Filter 9 | // Page 17 10 | 11 | class Image { 12 | int Rows; 13 | int Cols; 14 | char[] Data; 15 | Image(int w, int h){ 16 | Data = new char[w*h]; 17 | Rows = h; 18 | Cols = w; 19 | } 20 | Image(PImage img){ 21 | this(img.width,img.height); 22 | img.loadPixels(); 23 | for (int i = 0; i < img.pixels.length; i++){ 24 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 25 | } 26 | } 27 | PImage toPImage(){ 28 | PImage img = createImage(Cols,Rows,ARGB); 29 | img.loadPixels(); 30 | for (int i = 0; i < img.pixels.length; i++){ 31 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 32 | } 33 | img.updatePixels(); 34 | return img; 35 | } 36 | }; 37 | 38 | void MMSE_filter(Image IMAGE, Image IMAGE1, float NVAR){ 39 | int X, Y, x1, y1, N, g; 40 | int total, sum, sum1, R; 41 | float FSECOND, FVAR=0.0, FMEAN=0.0; 42 | R=IMAGE.Cols; 43 | N=5; 44 | for (Y=N/2;Y 255) g = 255; 66 | if (g < 0) g = 0; 67 | IMAGE1.Data[X+Y*R]=(char)g; 68 | } 69 | } 70 | } 71 | 72 | void setup(){ 73 | size(1024,512); 74 | PImage img = loadImage(sketchPath("../images/peppers.png")); 75 | Image IMAGE = new Image(img); 76 | Image IMAGE1 = new Image(img.width,img.height); 77 | float NSTD = 20; 78 | for (int i = 0; i < IMAGE.Data.length; i++){ 79 | IMAGE.Data[i] = (char)constrain(IMAGE.Data[i] + (randomGaussian() * NSTD),0,255); 80 | } 81 | 82 | MMSE_filter(IMAGE,IMAGE1,NSTD*NSTD); 83 | 84 | PImage img0 = IMAGE.toPImage(); 85 | PImage img1 = IMAGE1.toPImage(); 86 | 87 | image(img0,0,0); 88 | image(img1,512,0); 89 | 90 | save("preview.png"); 91 | } 92 | -------------------------------------------------------------------------------- /AdaptiveMMSE/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/AdaptiveMMSE/preview.png -------------------------------------------------------------------------------- /AlphaMeanFilter/AlphaMeanFilter.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Alpha-Trimmed Mean Filter 9 | // Page 21 10 | 11 | class Image { 12 | int Rows; 13 | int Cols; 14 | char[] Data; 15 | Image(int w, int h){ 16 | Data = new char[w*h]; 17 | Rows = h; 18 | Cols = w; 19 | } 20 | Image(PImage img){ 21 | this(img.width,img.height); 22 | img.loadPixels(); 23 | for (int i = 0; i < img.pixels.length; i++){ 24 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 25 | } 26 | } 27 | PImage toPImage(){ 28 | PImage img = createImage(Cols,Rows,ARGB); 29 | img.loadPixels(); 30 | for (int i = 0; i < img.pixels.length; i++){ 31 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 32 | } 33 | img.updatePixels(); 34 | return img; 35 | } 36 | }; 37 | 38 | void AlphaMean(Image IMAGE, int P, Image IMAGE1){ 39 | int X, Y, I, J, SUM, Z; 40 | int N, A; 41 | int[] AR = new int[121]; 42 | N = 7; 43 | for (Y=N/2; Y=0 && AR[I] >A){ 56 | AR[I+1]=AR[I]; 57 | I--; 58 | } 59 | AR[I+1]=A; 60 | } 61 | SUM = 0; Z = 0; 62 | for (J=P; J<=N*N-1-P;J++){ 63 | SUM = SUM + AR[J]; 64 | Z++; 65 | } 66 | IMAGE1.Data[X+Y*IMAGE.Cols]=(char) ((float)SUM/(float)Z+0.5); 67 | } 68 | } 69 | } 70 | 71 | void setup(){ 72 | size(1024,512); 73 | PImage img = loadImage(sketchPath("../images/peppers.png")); 74 | Image IMAGE = new Image(img); 75 | Image IMAGE1 = new Image(img.width,img.height); 76 | float NSTD = 20; 77 | for (int i = 0; i < IMAGE.Data.length; i++){ 78 | IMAGE.Data[i] = (char)constrain(IMAGE.Data[i] + (randomGaussian() * NSTD),0,255); 79 | } 80 | 81 | int P = 12; 82 | AlphaMean(IMAGE,P,IMAGE1); 83 | 84 | PImage img0 = IMAGE.toPImage(); 85 | PImage img1 = IMAGE1.toPImage(); 86 | 87 | image(img0,0,0); 88 | image(img1,512,0); 89 | 90 | save("preview.png"); 91 | } 92 | -------------------------------------------------------------------------------- /AlphaMeanFilter/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/AlphaMeanFilter/preview.png -------------------------------------------------------------------------------- /Centroid/Centroid.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Centroid 9 | // Page 31 (Area on Page 23) 10 | 11 | // Notes: 12 | // - It appears that the authors of the book confused 13 | // the correspondance of i/j and x/y in 14 | // the main loop of centroid() function. The error 15 | // has been corrected in this implementation, 16 | // and the loop is re-nested in a cache-friendly 17 | // manner. 18 | 19 | 20 | class Image { 21 | int Rows; 22 | int Cols; 23 | char[] Data; 24 | Image(int w, int h){ 25 | Data = new char[w*h]; 26 | Rows = h; 27 | Cols = w; 28 | } 29 | Image(PImage img){ 30 | this(img.width,img.height); 31 | img.loadPixels(); 32 | for (int i = 0; i < img.pixels.length; i++){ 33 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 34 | } 35 | } 36 | PImage toPImage(){ 37 | PImage img = createImage(Cols,Rows,ARGB); 38 | img.loadPixels(); 39 | for (int i = 0; i < img.pixels.length; i++){ 40 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 41 | } 42 | img.updatePixels(); 43 | return img; 44 | } 45 | }; 46 | 47 | int area(Image In, int x1, int y1, int x2, int y2, char ObjVal){ 48 | int i, j; 49 | int area_value = 0; 50 | for (i = y1; i <= y2; ++i){ 51 | for (j = x1; j <= x2; ++j){ 52 | if (In.Data[j+i*In.Cols]==ObjVal) ++ area_value; 53 | } 54 | } 55 | return area_value; 56 | } 57 | 58 | class coord { 59 | float x,y; 60 | }; 61 | 62 | void centroid(Image In, int x1, int y1, int x2, int y2, char ObjVal, coord coords){ 63 | int i, j; 64 | int area_value, Xcent = 0, Ycent = 0; 65 | area_value = area(In, x1, y1, x2, y2, ObjVal); 66 | if (area_value == 0){ 67 | coords.x = -1; coords.y = -1; 68 | return; 69 | } 70 | for (i = y1; i <= y2; ++i){ 71 | for (j = x1; j <= x2; ++j){ 72 | if (In.Data[i*In.Cols+j]==ObjVal){ 73 | Xcent += j; 74 | Ycent += i; 75 | } 76 | } 77 | } 78 | coords.x = Xcent/area_value + 1; 79 | coords.y = Ycent/area_value + 1; 80 | return; 81 | } 82 | 83 | void setup(){ 84 | size(512,512); 85 | PGraphics img = createGraphics(512,512); 86 | img.beginDraw(); 87 | img.noStroke(); 88 | img.background(0); 89 | img.ellipse(150,300,80,50); 90 | img.ellipse(170,260,60,130); 91 | img.rect(170,300,50,50); 92 | img.endDraw(); 93 | 94 | Image In = new Image(img); 95 | coord coords = new coord(); 96 | centroid(In,0,0,511,511,(char)255,coords); 97 | 98 | image(img,0,0); 99 | fill(255,0,0); 100 | noStroke(); 101 | circle(coords.x,coords.y,10); 102 | 103 | save("preview.png"); 104 | } 105 | -------------------------------------------------------------------------------- /Centroid/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/Centroid/preview.png -------------------------------------------------------------------------------- /Circularity/Circularity.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Circularity 9 | // Page 35 (Centroid on Page 31, Erosion on Page 76) 10 | 11 | // Notes: 12 | // - The circularity() function in this port 13 | // also computes the boundary pixels, while 14 | // the book seems to assume they're precomputed. 15 | // It is so modified because the centroid 16 | // needs to be computed on the "solid" image 17 | // while the circularity on the "hollow" outline 18 | // image. The authors of the book seem to be 19 | // oblivious of this issue (if I understood the 20 | // algorithm correctly that is). 21 | 22 | class Image { 23 | int Rows; 24 | int Cols; 25 | char[] Data; 26 | Image(int w, int h){ 27 | Data = new char[w*h]; 28 | Rows = h; 29 | Cols = w; 30 | } 31 | Image(PImage img){ 32 | this(img.width,img.height); 33 | img.loadPixels(); 34 | for (int i = 0; i < img.pixels.length; i++){ 35 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 36 | } 37 | } 38 | PImage toPImage(){ 39 | PImage img = createImage(Cols,Rows,ARGB); 40 | img.loadPixels(); 41 | for (int i = 0; i < img.pixels.length; i++){ 42 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 43 | } 44 | img.updatePixels(); 45 | return img; 46 | } 47 | }; 48 | 49 | int area(Image In, int x1, int y1, int x2, int y2, char ObjVal){ 50 | int i, j; 51 | int area_value = 0; 52 | for (i = y1; i <= y2; ++i){ 53 | for (j = x1; j <= x2; ++j){ 54 | if (In.Data[j+i*In.Cols]==ObjVal) ++ area_value; 55 | } 56 | } 57 | return area_value; 58 | } 59 | 60 | class coord { 61 | float x,y; 62 | }; 63 | 64 | void centroid(Image In, int x1, int y1, int x2, int y2, char ObjVal, coord coords){ 65 | int i, j; 66 | int area_value, Xcent = 0, Ycent = 0; 67 | area_value = area(In, x1, y1, x2, y2, ObjVal); 68 | if (area_value == 0){ 69 | coords.x = -1; coords.y = -1; 70 | return; 71 | } 72 | for (i = y1; i <= y2; ++i){ 73 | for (j = x1; j <= x2; ++j){ 74 | if (In.Data[i*In.Cols+j]==ObjVal){ 75 | Xcent += j; 76 | Ycent += i; 77 | } 78 | } 79 | } 80 | coords.x = Xcent/area_value + 1; 81 | coords.y = Ycent/area_value + 1; 82 | return; 83 | } 84 | 85 | 86 | void Erosion(Image IMAGE, int[][] MASK, Image FILTER){ 87 | int X,Y,I,J,smin=255; 88 | int N=MASK.length; 89 | for (Y=N/2; Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF); 32 | } 33 | } 34 | PImage toPImage(int normalizeMode){ 35 | PImage img = createImage(Cols,Rows,ARGB); 36 | img.loadPixels(); 37 | float[] mag = new float[img.pixels.length]; 38 | float M = 0; 39 | float m = 0; 40 | for (int i = 0; i < mag.length; i++){ 41 | mag[i] = sqrt(Data[i*2]*Data[i*2]+Data[i*2+1]*Data[i*2+1]); 42 | if (normalizeMode != 0){ 43 | mag[i] = log(1+mag[i]); 44 | } 45 | M = max(mag[i],M); 46 | m += mag[i]; 47 | } 48 | m/=mag.length; 49 | for (int i = 0; i < img.pixels.length; i++){ 50 | if (normalizeMode == 0){ 51 | img.pixels[i] = color(mag[i]); 52 | }else if (normalizeMode == 1){ 53 | img.pixels[i] = color(mag[i]/M*255); 54 | }else{ 55 | img.pixels[i] = color(mag[i]/m*32); 56 | } 57 | } 58 | img.updatePixels(); 59 | return img; 60 | } 61 | }; 62 | 63 | void DiscreteFourier(Image IMAGE, float dir){ 64 | int X, Y, num; 65 | int R; 66 | float[] data = new float[1024]; 67 | float scale; 68 | num = IMAGE.Rows; 69 | if (dir == 1.0){ 70 | scale = num*num; 71 | }else{ 72 | scale = 1.0; 73 | } 74 | 75 | for (Y=0; Y>j); 116 | } 117 | ind = ind<<1; j = i << 1; 118 | if (j < ind){ 119 | temp = data[j]; data[j] = data[ind]; 120 | data[ind] = temp; temp = data[j+1]; 121 | data[j+1] = data[ind+1]; 122 | data[ind+1] = temp; 123 | } 124 | } 125 | for (inc=2;inc> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF); 28 | Data[i]/=255; 29 | } 30 | } 31 | float sigmoid(float x, float a){ 32 | return (x<=0.5)?((pow(2.0*x, 1.0/(1-a)))/2.0):(1.0 - (pow(2.0*(1.0-x), 1.0/(1-a)))/2.0); 33 | } 34 | 35 | PImage toPImage(int normalizeMode){ 36 | PImage img = createImage(Cols,Rows,ARGB); 37 | img.loadPixels(); 38 | float m = Float.POSITIVE_INFINITY; 39 | float M = Float.NEGATIVE_INFINITY; 40 | for (int i = 0; i < Data.length; i++){ 41 | M = max(Data[i],M); 42 | m = min(Data[i],m); 43 | } 44 | float MMm = max(abs(M),abs(m)); 45 | for (int i = 0; i < img.pixels.length; i++){ 46 | if (normalizeMode == 0){ 47 | img.pixels[i] = color(Data[i]*255); 48 | }else{ 49 | img.pixels[i] = color(sigmoid((Data[i]+MMm)/(MMm*2),0.95)*255); 50 | } 51 | } 52 | img.updatePixels(); 53 | return img; 54 | } 55 | }; 56 | 57 | void DiscreteCosine(Image IMAGE, Image IMAGE1, int dir){ 58 | int X, Y, n, m, num; 59 | float sum, pi, k0, k1, ktx, kty, A; 60 | pi = 3.141592654; 61 | num = IMAGE.Rows; 62 | k0 = sqrt(1.0/(float)num); 63 | k1 = sqrt(2.0/(float)num); 64 | for (m = 0; m < num; m++){ 65 | for (n = 0; n < num; n++){ 66 | sum = 0.0; 67 | for (Y = 0; Y < num; Y++){ 68 | if (dir == 1){ 69 | A = cos((float)((2.0*(float)Y+1)*m*pi/2.0/num)); 70 | }else{ 71 | A = cos((float)((2.0*(float)m+1)*Y*pi/2.0/num)); 72 | } 73 | for (X = 0; X < num; X++){ 74 | if (dir == -1){ 75 | if (X == 0) ktx = k0; else ktx = k1; 76 | if (Y == 0) kty = k0; else kty = k1; 77 | sum = sum + IMAGE.Data[X+Y*IMAGE.Rows]*cos((float)((2.0*(float)n+1)*X*pi/2.0/(float)num))*A*ktx*kty; 78 | }else{ 79 | ktx = 1; 80 | kty = 1; 81 | sum = sum + IMAGE.Data[X+Y*IMAGE.Rows]*cos((float)((2.0*(float)X+1)*n*pi/2.0/(float)num))*A*ktx*kty; 82 | 83 | } 84 | } 85 | } 86 | if (dir == 1){ 87 | if (n == 0) sum *= k0; else sum *= k1; 88 | if (m == 0) sum *= k0; else sum *= k1; 89 | } 90 | IMAGE1.Data[n+m*IMAGE.Rows]=sum; 91 | } 92 | } 93 | } 94 | 95 | 96 | void setup(){ 97 | size(768,268); 98 | PImage img,img1; 99 | 100 | Image IMAGE, IMAGE1; 101 | noSmooth(); 102 | textSize(12); 103 | noStroke(); 104 | fill(0); 105 | 106 | img = loadImage(sketchPath("../images/cameraman.png")); 107 | img.resize(64,64); 108 | 109 | IMAGE = new Image(img); 110 | IMAGE1 = new Image(img.width,img.height); 111 | 112 | println("generating DCT..."); 113 | DiscreteCosine(IMAGE,IMAGE1,1); 114 | img1 = IMAGE1.toPImage(1); 115 | 116 | image(img,0,0,256,256); 117 | image(img1,256,0,256,256); 118 | 119 | println("reconstructing..."); 120 | DiscreteCosine(IMAGE1,IMAGE,-1); 121 | PImage img2 = IMAGE.toPImage(0); 122 | image(img2,512,0,256,256); 123 | 124 | text("Original",0,266); 125 | text("DCT",256,266); 126 | text("Reconstructed",512,266); 127 | 128 | save("preview.png"); 129 | } 130 | -------------------------------------------------------------------------------- /DiscreteCosineTransform/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/DiscreteCosineTransform/preview.png -------------------------------------------------------------------------------- /DiscreteFourierTransform/DiscreteFourierTransform.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Discrete Fourier Transform 9 | // Page 70 10 | 11 | // Notes: 12 | // - An additional fftshift() function is implemented 13 | // to center the FFT results, so the visualization look 14 | // nice and familiar. 15 | 16 | class Image {//Square, Complex Image 17 | int Rows;//<=512 18 | int Cols;//==Rows<=512 19 | float[] Data; 20 | Image(int w, int h){ 21 | Data = new float[w*h*2]; 22 | Rows = h; 23 | Cols = w; 24 | } 25 | Image(PImage img){ 26 | this(img.width,img.height); 27 | img.loadPixels(); 28 | for (int i = 0; i < img.pixels.length; i++){ 29 | Data[i*2] = 0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF); 30 | Data[i*2]/=255; 31 | } 32 | } 33 | PImage toPImage(int normalizeMode){ 34 | PImage img = createImage(Cols,Rows,ARGB); 35 | img.loadPixels(); 36 | float[] mag = new float[img.pixels.length]; 37 | float M = 0; 38 | float m = 0; 39 | for (int i = 0; i < mag.length; i++){ 40 | mag[i] = sqrt(Data[i*2]*Data[i*2]+Data[i*2+1]*Data[i*2+1]); 41 | if (normalizeMode != 0){ 42 | mag[i] = log(1+mag[i]); 43 | } 44 | M = max(mag[i],M); 45 | m += mag[i]; 46 | } 47 | m/=mag.length; 48 | for (int i = 0; i < img.pixels.length; i++){ 49 | if (normalizeMode == 0){ 50 | img.pixels[i] = color(mag[i]*255); 51 | }else if (normalizeMode == 1){ 52 | img.pixels[i] = color(mag[i]/M*255); 53 | }else{ 54 | img.pixels[i] = color(mag[i]/m*32); 55 | } 56 | } 57 | img.updatePixels(); 58 | return img; 59 | } 60 | }; 61 | 62 | void DiscreteFourier(Image IMAGE, float dir){ 63 | int X, Y, num; 64 | int R; 65 | float[] data = new float[1024]; 66 | float scale; 67 | num = IMAGE.Rows; 68 | if (dir == 1.0){ 69 | scale = num*num; 70 | }else{ 71 | scale = 1.0; 72 | } 73 | if (dir==-1.0) fftshift(IMAGE); 74 | 75 | for (Y=0; Y>j); 117 | } 118 | ind = ind<<1; j = i << 1; 119 | if (j < ind){ 120 | temp = data[j]; data[j] = data[ind]; 121 | data[ind] = temp; temp = data[j+1]; 122 | data[j+1] = data[ind+1]; 123 | data[ind+1] = temp; 124 | } 125 | } 126 | for (inc=2;inc> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 29 | } 30 | } 31 | PImage toPImage(){ 32 | PImage img = createImage(Cols,Rows,ARGB); 33 | img.loadPixels(); 34 | for (int i = 0; i < img.pixels.length; i++){ 35 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 36 | } 37 | img.updatePixels(); 38 | return img; 39 | } 40 | }; 41 | 42 | void Gaussian(Image IMAGE, float VAR, float MEAN){ 43 | int X, Y; 44 | float NOISE, theta; 45 | for (Y = 0; Y 255) NOISE = 255; 52 | if (NOISE < 0) NOISE = 0; 53 | IMAGE.Data[X+Y*IMAGE.Cols]= (char)(NOISE+0.5); 54 | } 55 | } 56 | } 57 | 58 | void setup(){ 59 | size(513,256); 60 | 61 | Image IMAGE = new Image(256,256); 62 | float stdev = 16; 63 | 64 | Gaussian(IMAGE,stdev*stdev,128); 65 | 66 | PImage img0 = IMAGE.toPImage(); 67 | 68 | image(img0,0,0); 69 | text("Pocket Handbook Gaussian()",0,10); 70 | 71 | // Compare with Processing randomGaussian(): 72 | // (which seems to be an alias of Java Random.nextGaussian()) 73 | 74 | PImage img1 = new PImage(256,256); 75 | img1.loadPixels(); 76 | for (int i = 0; i < img1.pixels.length; i++){ 77 | img1.pixels[i] = color(randomGaussian()*stdev+128); 78 | } 79 | img1.updatePixels(); 80 | image(img1,257,0); 81 | text("Processing randomGaussian()",257,10); 82 | 83 | save("preview.png"); 84 | } 85 | -------------------------------------------------------------------------------- /GaussianNoise/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/GaussianNoise/preview.png -------------------------------------------------------------------------------- /GeometricMeanFilter/GeometricMeanFilter.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Geometric Mean Filter 9 | // Page 88 10 | 11 | // Notes: 12 | // - Due to geometric mean, black pixels in 13 | // the input image introduces black square 14 | // artifacts. Here a max(1,gray) trick is 15 | // added to ensure this does not happen. 16 | 17 | class Image { 18 | int Rows; 19 | int Cols; 20 | char[] Data; 21 | Image(int w, int h){ 22 | Data = new char[w*h]; 23 | Rows = h; 24 | Cols = w; 25 | } 26 | Image(PImage img){ 27 | this(img.width,img.height); 28 | img.loadPixels(); 29 | for (int i = 0; i < img.pixels.length; i++){ 30 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 31 | } 32 | } 33 | PImage toPImage(){ 34 | PImage img = createImage(Cols,Rows,ARGB); 35 | img.loadPixels(); 36 | for (int i = 0; i < img.pixels.length; i++){ 37 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 38 | } 39 | img.updatePixels(); 40 | return img; 41 | } 42 | }; 43 | 44 | void GeometricMean(Image IMAGE, Image IMAGE1){ 45 | int X, Y, I, J, Z; 46 | int N; 47 | int[] AR = new int[121]; 48 | float PRODUCT; 49 | float[] TAR = new float[121]; 50 | N = 5; 51 | for (Y=N/2; Y 255){ 68 | IMAGE1.Data[X+Y*IMAGE.Cols]=255; 69 | }else{ 70 | IMAGE1.Data[X+Y*IMAGE.Cols]=(char)PRODUCT; 71 | } 72 | } 73 | } 74 | } 75 | 76 | void setup(){ 77 | size(1024,512); 78 | PImage img = loadImage(sketchPath("../images/peppers.png")); 79 | Image IMAGE = new Image(img); 80 | Image IMAGE1 = new Image(img.width,img.height); 81 | float NSTD = 20; 82 | for (int i = 0; i < IMAGE.Data.length; i++){ 83 | IMAGE.Data[i] = (char)constrain(IMAGE.Data[i] + (randomGaussian() * NSTD),0,255); 84 | } 85 | 86 | GeometricMean(IMAGE,IMAGE1); 87 | 88 | PImage img0 = IMAGE.toPImage(); 89 | PImage img1 = IMAGE1.toPImage(); 90 | 91 | image(img0,0,0); 92 | image(img1,512,0); 93 | 94 | save("preview.png"); 95 | 96 | } 97 | -------------------------------------------------------------------------------- /GeometricMeanFilter/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/GeometricMeanFilter/preview.png -------------------------------------------------------------------------------- /HadamardTransform/HadamardTransform.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Hadamard Transform 9 | // Page 99 10 | 11 | // Notes: 12 | // - B=malloc(...) seems to be never freed 13 | // (not that it matters in this Java port) 14 | 15 | class Image {//Square, Float Image 16 | int Rows;// 17 | int Cols;//==Rows 18 | float[] Data; 19 | Image(int w, int h){ 20 | Data = new float[w*h]; 21 | Rows = h; 22 | Cols = w; 23 | } 24 | Image(PImage img){ 25 | this(img.width,img.height); 26 | img.loadPixels(); 27 | for (int i = 0; i < img.pixels.length; i++){ 28 | Data[i] = 0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF); 29 | Data[i]/=255; 30 | } 31 | } 32 | float sigmoid(float x, float a){ 33 | return (x<=0.5)?((pow(2.0*x, 1.0/(1-a)))/2.0):(1.0 - (pow(2.0*(1.0-x), 1.0/(1-a)))/2.0); 34 | } 35 | 36 | PImage toPImage(int normalizeMode){ 37 | PImage img = createImage(Cols,Rows,ARGB); 38 | img.loadPixels(); 39 | float m = Float.POSITIVE_INFINITY; 40 | float M = Float.NEGATIVE_INFINITY; 41 | for (int i = 0; i < Data.length; i++){ 42 | M = max(Data[i],M); 43 | m = min(Data[i],m); 44 | } 45 | float MMm = max(abs(M),abs(m)); 46 | for (int i = 0; i < img.pixels.length; i++){ 47 | if (normalizeMode == 0){ 48 | img.pixels[i] = color(Data[i]*255); 49 | }else{ 50 | img.pixels[i] = color(sigmoid((Data[i]+MMm)/(MMm*2),0.95)*255); 51 | } 52 | } 53 | img.updatePixels(); 54 | return img; 55 | } 56 | }; 57 | 58 | void bitrep(char[] b, int q, int num){ 59 | int x, i, bit; 60 | for (x = 0; x < num; x++){ 61 | bit = 1; 62 | for (i = 0; i < q; i++){ 63 | b[i+x*q] = (char)((x&bit)/bit); 64 | bit = bit << 1; 65 | } 66 | } 67 | } 68 | 69 | void Hadamard(Image IMAGE, Image IMAGE1, int dir){ 70 | int X, Y, n, m, num, I, q; 71 | int sum1, temp; 72 | char[] B; 73 | float K0, sum; 74 | num = IMAGE.Rows; 75 | q = (int)(log((float)IMAGE.Rows) / log(2.0)+0.5); 76 | B = new char[num*q]; 77 | bitrep(B,q,num); 78 | K0=num*num; 79 | for (m = 0; m < num; m++){ 80 | for (n = 0; n < num; n++){ 81 | sum = 0; 82 | for (Y = 0; Y < num; Y++){ 83 | for (X = 0; X < num; X++){ 84 | sum1 = 0; 85 | for (I = 0; I <= q-1; I++){ 86 | sum1 += B[I+X*q]* B[I+n*q] + B[I+Y*q] * B[I+m*q]; 87 | } 88 | if ((sum1/2)*2==sum1) temp = 1; else temp = -1; 89 | sum += IMAGE.Data[X+Y*IMAGE.Rows]*temp; 90 | } 91 | } 92 | IMAGE1.Data[n+m*IMAGE.Rows]=sum; 93 | } 94 | } 95 | if (dir == 1){ 96 | for (Y=0; Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 31 | } 32 | } 33 | PImage toPImage(){ 34 | PImage img = createImage(Cols,Rows,ARGB); 35 | img.loadPixels(); 36 | for (int i = 0; i < img.pixels.length; i++){ 37 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 38 | } 39 | img.updatePixels(); 40 | return img; 41 | } 42 | }; 43 | 44 | void HarmonicMean(Image IMAGE, Image IMAGE1){ 45 | int X, Y, I; 46 | int J, Z; 47 | int N, A; 48 | int[] AR = new int[121]; 49 | float SUM; 50 | N = 5; 51 | for (Y=N/2; Y255){ 74 | A = 255; 75 | } 76 | IMAGE1.Data[X+Y*IMAGE.Cols]=(char)A; 77 | 78 | } 79 | } 80 | } 81 | } 82 | 83 | void setup(){ 84 | size(1024,512); 85 | PImage img = loadImage(sketchPath("../images/peppers.png")); 86 | Image IMAGE = new Image(img); 87 | Image IMAGE1 = new Image(img.width,img.height); 88 | float NSTD = 20; 89 | for (int i = 0; i < IMAGE.Data.length; i++){ 90 | IMAGE.Data[i] = (char)constrain(IMAGE.Data[i] + (randomGaussian() * NSTD),0,255); 91 | } 92 | 93 | HarmonicMean(IMAGE,IMAGE1); 94 | 95 | PImage img0 = IMAGE.toPImage(); 96 | PImage img1 = IMAGE1.toPImage(); 97 | 98 | image(img0,0,0); 99 | image(img1,512,0); 100 | 101 | save("preview.png"); 102 | 103 | } 104 | -------------------------------------------------------------------------------- /HarmonicMeanFilter/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/HarmonicMeanFilter/preview.png -------------------------------------------------------------------------------- /HartleyTransform/HartleyTransform.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Hartley Transform 9 | // Page 106 10 | 11 | // Notes: 12 | // - The code in the book seems to be a poor C translation 13 | // of the reference paper (Reeves)'s Pascal implementation. 14 | // There're many errors throughout (apparently the authors 15 | // didn't bother to actually run their code). Hopefully this port 16 | // manages to address all of them. 17 | // 18 | // 1 The handbook authors confused Pascal's "div" with "mod", hence 19 | // all of the % operators need to be changed back to /. Otherwise 20 | // the entirety of the function will be skipped. 21 | // 22 | // 2 In the first "gpNum" for loop, "gpNum" should be incremented 23 | // instead of "i" (++i -> ++gpNum), otherwise it becomes an infinite 24 | // loop repeating the first iteration forever. 25 | // 26 | // 3 Reeves describes a "bit reversal" algorithm, the effects of which 27 | // seems to be acheived in a different way by the handbook authors. 28 | // Unfortunately, the handbook version accesses out of bound indices 29 | // and crashes Java. Fortunately, after changing the line 30 | // "n=length<<1" to "n=length", it starts to behave identically to the 31 | // orignal bit reversal. Both methods are included in this port 32 | // for reference. 33 | // 34 | // - The algorithm is supposedly a 1D Hartley transform, but this demo 35 | // runs it on a flattened 2D image, for easier verification of correctness. 36 | // The resultant 2D pattern from the 1D pass nevertheless looks interesting. 37 | // 38 | // - TODO: figure out the 2D Harley transform 39 | 40 | void hartley(float In[], int length, boolean dir){ 41 | 42 | int stage, gpNum, gpIndex, gpSize, numGps, Nl2; 43 | int n,i,j,m; 44 | int bfNum, numBfs; 45 | int Ad0, Ad1, Ad2, Ad3, Ad4, CSAd; 46 | float[] C = new float[length]; 47 | float[] S = new float[length]; 48 | float rt1,rt2,rt3,rt4,tmp,theta,dTheta,pi; 49 | pi = PI; 50 | theta = 0; 51 | dTheta = 2 * pi / length; 52 | for (i = 0; i < length/4; ++i){ 53 | C[i] = cos(theta); 54 | S[i] = sin(theta); 55 | theta += dTheta; 56 | } 57 | Nl2 = (int)(log(length)/log(2)); 58 | n = length; 59 | j = 1; 60 | for (i=1; ii){ 62 | tmp = In[j-1]; 63 | In[j-1] = In[i-1]; 64 | In[i-1] = tmp; 65 | } 66 | m = n>>1; 67 | while (m >= 2 && j > m){ 68 | j -= m; 69 | m >>= 1; 70 | } 71 | j += m; 72 | } 73 | //alternatively: 74 | //BitRevRArr(In,Nl2,length); 75 | 76 | gpSize = 2; 77 | numGps = length / 4; 78 | 79 | for (gpNum = 0; gpNum < numGps - 1; ++gpNum){ 80 | Ad1 = gpNum * 4; 81 | Ad2 = Ad1 + 1; 82 | Ad3 = Ad1 + gpSize; 83 | Ad4 = Ad2 + gpSize; 84 | rt1 = In[Ad1]+In[Ad2]; 85 | rt2 = In[Ad1]-In[Ad2]; 86 | rt3 = In[Ad3]+In[Ad4]; 87 | rt4 = In[Ad3]-In[Ad4]; 88 | In[Ad1]=rt1+rt3; 89 | In[Ad2]=rt2+rt4; 90 | In[Ad3]=rt1-rt3; 91 | In[Ad4]=rt2-rt4; 92 | } 93 | if (Nl2 > 2){ 94 | gpSize = 4; 95 | numBfs = 2; 96 | numGps = numGps / 2; 97 | for (stage = 2; stage < Nl2; ++stage){ 98 | for (gpNum = 0; gpNum < numGps; ++ gpNum){ 99 | 100 | Ad0 = gpNum * gpSize * 2; 101 | Ad1 = Ad0; 102 | Ad2 = Ad1 + gpSize; 103 | Ad3 = Ad1 + gpSize / 2; 104 | Ad4 = Ad3 + gpSize; 105 | rt1 = In[Ad1]; 106 | In[Ad1] = In[Ad1] + In[Ad2]; 107 | In[Ad2] = rt1 - In[Ad2]; 108 | rt1 = In[Ad3]; 109 | In[Ad3] = In[Ad3] + In[Ad4]; 110 | In[Ad4] = rt1 - In[Ad4]; 111 | for (bfNum = 1; bfNum>i)&1) == 1){ 152 | temp |= 1<<(bitlen-i-1); 153 | } 154 | } 155 | return temp & 0xFFFF; 156 | } 157 | void BitRevRArr(float[] x, int bitlen, int maxN){ 158 | int i; 159 | float[] tempArr = new float[x.length]; 160 | for (i = 0; i < maxN; i++){ 161 | tempArr[i] = x[BitRevX(i,bitlen)]; 162 | } 163 | System.arraycopy(tempArr,0,x,0,maxN); 164 | } 165 | 166 | void setup(){ 167 | size(768,256); 168 | PImage img; 169 | 170 | float[] In; 171 | noSmooth(); 172 | 173 | img = loadImage(sketchPath("../images/cameraman.png")); 174 | img.resize(256,256); 175 | 176 | image(img,0,0); 177 | 178 | img.loadPixels(); 179 | In = new float[img.pixels.length]; 180 | for (int i = 0; i < img.pixels.length; i++){ 181 | In[i] = 0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF); 182 | In[i] /= 255; 183 | } 184 | 185 | hartley(In,In.length,true); 186 | 187 | for (int i = 0; i < img.pixels.length; i++){ 188 | img.pixels[i] = color(128+In[i]*5); 189 | } 190 | img.updatePixels(); 191 | 192 | image(img,256,0); 193 | 194 | img.loadPixels(); 195 | 196 | hartley(In,In.length,false); 197 | for (int i = 0; i < img.pixels.length; i++){ 198 | img.pixels[i] = color(In[i]*255); 199 | } 200 | img.updatePixels(); 201 | 202 | image(img,512,0); 203 | 204 | save("preview.png"); 205 | 206 | } 207 | -------------------------------------------------------------------------------- /HartleyTransform/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/HartleyTransform/preview.png -------------------------------------------------------------------------------- /HistogramEqualization/HistogramEqualization.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Histogram Equalization 9 | // Page 110 (Graylevel Histogram on page 96) 10 | 11 | class Image { 12 | int Rows; 13 | int Cols; 14 | char[] Data; 15 | Image(int w, int h){ 16 | Data = new char[w*h]; 17 | Rows = h; 18 | Cols = w; 19 | } 20 | Image(PImage img){ 21 | this(img.width,img.height); 22 | img.loadPixels(); 23 | for (int i = 0; i < img.pixels.length; i++){ 24 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 25 | } 26 | } 27 | PImage toPImage(){ 28 | PImage img = createImage(Cols,Rows,ARGB); 29 | img.loadPixels(); 30 | for (int i = 0; i < img.pixels.length; i++){ 31 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 32 | } 33 | img.updatePixels(); 34 | return img; 35 | } 36 | }; 37 | 38 | void Histogram(Image IMAGE, float[] HIST){ 39 | int X, Y, I, J; 40 | int[] IHIST = new int[256]; 41 | int SUM; 42 | for (I=0;I<=255;I++)IHIST[I]=0; 43 | SUM=0; 44 | for (Y=0;Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 25 | } 26 | } 27 | PImage toPImage(){ 28 | PImage img = createImage(Cols,Rows,ARGB); 29 | img.loadPixels(); 30 | for (int i = 0; i < img.pixels.length; i++){ 31 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 32 | } 33 | img.updatePixels(); 34 | return img; 35 | } 36 | }; 37 | 38 | void Histogram(Image IMAGE, float[] HIST){ 39 | int X, Y, I, J; 40 | int[] IHIST = new int[256]; 41 | int SUM; 42 | for (I=0;I<=255;I++)IHIST[I]=0; 43 | SUM=0; 44 | for (Y=0;Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 38 | //Data[i]/=255; 39 | } 40 | } 41 | PImage toPImage(){ 42 | PImage img = createImage(Cols,Rows,ARGB); 43 | img.loadPixels(); 44 | for (int i = 0; i < img.pixels.length; i++){ 45 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 46 | } 47 | img.updatePixels(); 48 | return img; 49 | } 50 | }; 51 | 52 | 53 | float cmoment(int p, int q, Image In, int x1, int y1, int x2, int y2){ 54 | int i,j,xb,yb; 55 | float m00=0.0,m10=0.0,m01=0.0,upq=0.0; 56 | 57 | if ((p==1 && q==0) || (p==0 && q==1)) 58 | return upq; 59 | 60 | for (i = y1; i < y2; ++i) 61 | for (j = x1; j < x2; ++j) 62 | m00 += In.Data[i*In.Cols+j]; 63 | 64 | if (p == 0 && q == 0) return m00; 65 | 66 | for (i = y1; i < y2; ++i) 67 | for (j = x1; j < x2; ++j) 68 | m10 += j * In.Data[i*In.Cols+j]; 69 | 70 | if (p==1 && q==0) return m10; 71 | 72 | for (i = y1; i < y2; ++i) 73 | for (j = y1; j < y2; ++j) 74 | m01 += i * In.Data[i*In.Cols+j]; 75 | 76 | if (p==0 && q==1) return m01; 77 | 78 | xb = (int)floor(0.5+m10/m00); 79 | yb = (int)floor(0.5+m01/m00); 80 | 81 | if (p == 0){ 82 | for (i=y1; i 0){ 109 | return atan(((-b)+sqrt(b*b+4.0))/2.0); 110 | }else{ 111 | return atan(((-b)-sqrt(b*b+4.0))/2.0); 112 | } 113 | 114 | 115 | } 116 | 117 | 118 | 119 | void setup(){ 120 | size(256,256); 121 | 122 | } 123 | void draw(){ 124 | 125 | PGraphics img = createGraphics(256,256); 126 | img.beginDraw(); 127 | 128 | img.background(0); 129 | img.translate(128,128); 130 | img.rotate(0.01*frameCount); 131 | img.noStroke(); 132 | img.ellipse(0,0,200,40); 133 | 134 | img.rect(-30,0,60,60); 135 | 136 | img.endDraw(); 137 | 138 | image(img,0,0); 139 | 140 | Image In = new Image(img); 141 | 142 | float a = max_axis_slope(In,0,0,img.width,img.height); 143 | 144 | strokeWeight(2); 145 | 146 | pushMatrix(); 147 | translate(128,128); 148 | rotate(a); 149 | stroke(255,0,0); 150 | line(-150,0,150,0); 151 | popMatrix(); 152 | 153 | 154 | save("preview.png"); 155 | } 156 | -------------------------------------------------------------------------------- /MaximumAxis/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/MaximumAxis/preview.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | //| =============== | 3 | //| | THE | | 4 | //| POCKET HANDBOOK | 5 | //|OF IMAGE PROCESSING| 6 | //| ALGORITHMS IN C | 7 | //| | | | 8 | ``` 9 | 10 | [Processing](https://processing.org/) demos made when reading the book [*The Pocket Handbook of Image Processing Algorithms in C* (Harley R. Myler & Arthur R. Weeks)](http://adaptiveart.eecs.umich.edu/2011/wp-content/uploads/2011/09/The-pocket-handbook-of-image-processing-algorithms-in-C.pdf). 11 | 12 | Featuring: 13 | 14 | - C listings in the book ported to Processing. 15 | - Each algorithm is a standalone Processing sketch with no inter-dependency. 16 | - Each sketch includes a usage example. 17 | - Bugs in the book are fixed, and explained in the header comments. 18 | - Other notes/thoughts about the algorithms/implementations are also included in header comments. 19 | - The original "C89" flavour is preserved. 20 | - Each sketch folder includes a `preview.png` to show what to expect without running the sketch. 21 | 22 | Some trivial topics (e.g. flipping an image) are currently being skipped, but may occur as part of the code for another algorithm. 23 | 24 | A work in progress. 25 | 26 | Thanks to @golanlevin for gifting me with the (physical) book :) 27 | 28 | 29 | | | | 30 | |---|---| 31 | | [![](./AdaptiveDWMTM/preview.png)](./AdaptiveDWMTM) | [![](./AdaptiveMMSE/preview.png)](./AdaptiveMMSE) | 32 | | [AdaptiveDWMTM](./AdaptiveDWMTM) | [AdaptiveMMSE](./AdaptiveMMSE) | 33 | | [![](./AlphaMeanFilter/preview.png)](./AlphaMeanFilter) | [![](./Centroid/preview.png)](./Centroid) | 34 | | [AlphaMeanFilter](./AlphaMeanFilter) | [Centroid](./Centroid) | 35 | | [![](./Circularity/preview.png)](./Circularity) | [![](./CircularlySymmetricFilter/preview.png)](./CircularlySymmetricFilter) | 36 | | [Circularity](./Circularity) | [CircularlySymmetricFilter](./CircularlySymmetricFilter) | 37 | | [![](./DiscreteCosineTransform/preview.png)](./DiscreteCosineTransform) | [![](./DiscreteFourierTransform/preview.png)](./DiscreteFourierTransform) | 38 | | [DiscreteCosineTransform](./DiscreteCosineTransform) | [DiscreteFourierTransform](./DiscreteFourierTransform) | 39 | | [![](./GaussianNoise/preview.png)](./GaussianNoise) | [![](./GeometricMeanFilter/preview.png)](./GeometricMeanFilter) | 40 | | [GaussianNoise](./GaussianNoise) | [GeometricMeanFilter](./GeometricMeanFilter) | 41 | | [![](./HadamardTransform/preview.png)](./HadamardTransform) | [![](./HarmonicMeanFilter/preview.png)](./HarmonicMeanFilter) | 42 | | [HadamardTransform](./HadamardTransform) | [HarmonicMeanFilter](./HarmonicMeanFilter) | 43 | | [![](./HartleyTransform/preview.png)](./HartleyTransform) | [![](./HistogramEqualization/preview.png)](./HistogramEqualization) | 44 | | [HartleyTransform](./HartleyTransform) | [HistogramEqualization](./HistogramEqualization) | 45 | | [![](./HistogramSpecification/preview.png)](./HistogramSpecification) | [![](./MaximumAxis/preview.png)](./MaximumAxis) | 46 | | [HistogramSpecification](./HistogramSpecification) | [MaximumAxis](./MaximumAxis) | 47 | | [![](./Skeleton/preview.png)](./Skeleton) | [![](./Thickening/preview.png)](./Thickening) | 48 | | [Skeleton](./Skeleton) | [Thickening](./Thickening) | 49 | | [![](./Thinning/preview.png)](./Thinning) | [![](./TopHatFilter/preview.png)](./TopHatFilter) | 50 | | [Thinning](./Thinning) | [TopHatFilter](./TopHatFilter) | 51 | | [![](./Warping/preview.png)](./Warping) | 52 | | [Warping](./Warping) | -------------------------------------------------------------------------------- /Skeleton/Skeleton.pde: -------------------------------------------------------------------------------- 1 | //| =============== | 2 | //| | THE | | 3 | //| POCKET HANDBOOK | 4 | //|OF IMAGE PROCESSING| 5 | //| ALGORITHMS IN C | 6 | //| | | | 7 | 8 | // Skeleton 9 | // Page 210 10 | 11 | // Notes: 12 | // - For a more sophisticated algorithm with "cleaner" results, 13 | // see Zhang-Suen 1984 paper: 14 | // http://agcggs680.pbworks.com/f/Zhan-Suen_algorithm.pdf 15 | 16 | int N = 3; 17 | 18 | class Image { 19 | int Rows; 20 | int Cols; 21 | char[] Data; 22 | Image(int w, int h){ 23 | Data = new char[w*h]; 24 | Rows = h; 25 | Cols = w; 26 | } 27 | Image(PImage img){ 28 | this(img.width,img.height); 29 | img.loadPixels(); 30 | for (int i = 0; i < img.pixels.length; i++){ 31 | Data[i] = (0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)) > 127 ? (char)255 : (char)0; 32 | } 33 | } 34 | PImage toPImage(){ 35 | PImage img = createImage(Cols,Rows,ARGB); 36 | img.loadPixels(); 37 | for (int i = 0; i < img.pixels.length; i++){ 38 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 39 | } 40 | img.updatePixels(); 41 | return img; 42 | } 43 | }; 44 | 45 | void Erosion(Image IMAGE, int[][] MASK, Image FILTER){ 46 | int X,Y,I,J,smin=255; 47 | int N=MASK.length; 48 | for (Y=N/2; Ysmax){ 75 | smax = IMAGE.Data[X+I+(Y+J)*IMAGE.Cols]; 76 | } 77 | } 78 | } 79 | } 80 | FILTER.Data[X+Y*IMAGE.Cols]=(char)smax; 81 | } 82 | } 83 | } 84 | 85 | 86 | 87 | void Skeleton_(Image IMAGE, int[][] MASK, Image SKELETON){ 88 | int X, Y; 89 | int pixel; 90 | boolean pixel_on; 91 | Image FILTER, FILTER1; 92 | FILTER = new Image(IMAGE.Cols,IMAGE.Rows); 93 | FILTER1 = new Image(IMAGE.Cols,IMAGE.Rows); 94 | 95 | pixel_on = true; 96 | while (pixel_on){ 97 | pixel_on = false; 98 | Erosion(IMAGE,MASK,FILTER); 99 | Dilation(FILTER,MASK,FILTER1); 100 | for (Y=N/2;Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)) > 127 ? (char)255 : (char)0; 41 | } 42 | } 43 | PImage toPImage(){ 44 | PImage img = createImage(Cols,Rows,ARGB); 45 | img.loadPixels(); 46 | for (int i = 0; i < img.pixels.length; i++){ 47 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 48 | } 49 | img.updatePixels(); 50 | return img; 51 | } 52 | }; 53 | 54 | 55 | void Erosion(Image IMAGE, int[][] MASK, Image FILTER){ 56 | int X,Y,I,J,smin=255; 57 | int N=MASK.length; 58 | for (Y=N/2; Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)) > 127 ? (char)255 : (char)0; 28 | } 29 | } 30 | PImage toPImage(){ 31 | PImage img = createImage(Cols,Rows,ARGB); 32 | img.loadPixels(); 33 | for (int i = 0; i < img.pixels.length; i++){ 34 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 35 | } 36 | img.updatePixels(); 37 | return img; 38 | } 39 | }; 40 | 41 | 42 | void Erosion(Image IMAGE, int[][] MASK, Image FILTER){ 43 | int X,Y,I,J,smin=255; 44 | int N=MASK.length; 45 | for (Y=N/2; Y> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 28 | } 29 | } 30 | PImage toPImage(){ 31 | PImage img = createImage(Cols,Rows,ARGB); 32 | img.loadPixels(); 33 | for (int i = 0; i < img.pixels.length; i++){ 34 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 35 | } 36 | img.updatePixels(); 37 | return img; 38 | } 39 | }; 40 | 41 | 42 | void ErosionGray(Image IMAGE, int[][] MASK, Image FILTER){ 43 | int[][] a = new int[N][N]; 44 | int X, Y, I, J, smin; 45 | for (Y=N/2; Ysmax){ 80 | smax = a[I+N/2][J+N/2]; 81 | } 82 | } 83 | } 84 | if (smax>255) smax = 255; 85 | FILTER.Data[X+Y*IMAGE.Cols]=(char)smax; 86 | } 87 | } 88 | } 89 | 90 | void OpenGray(Image IMAGE, int[][] MASK, Image FILTER){ 91 | int X, Y; 92 | ErosionGray(IMAGE, MASK, FILTER); 93 | for (Y=0; Y 2 anywhere, black holes start showing 24 | // up. 25 | // Perhaps a better algorithm should iterate the pixels of 26 | // the output image instead, and apply the inverse transform 27 | // to find the corresponding color in the original image. 28 | // - Perhaps barycentric coordinates is one such alternative. 29 | 30 | class Image { 31 | int Rows; 32 | int Cols; 33 | char[] Data; 34 | Image(int w, int h){ 35 | Data = new char[w*h]; 36 | Rows = h; 37 | Cols = w; 38 | } 39 | Image(PImage img){ 40 | this(img.width,img.height); 41 | img.loadPixels(); 42 | for (int i = 0; i < img.pixels.length; i++){ 43 | Data[i] = (char)(0.21 * (float)(img.pixels[i] >> 16 & 0xFF) + 0.72 * (float)(img.pixels[i] >> 8 & 0xFF) + 0.07 * (float)(img.pixels[i] & 0xFF)); 44 | } 45 | } 46 | PImage toPImage(){ 47 | PImage img = createImage(Cols,Rows,ARGB); 48 | img.loadPixels(); 49 | for (int i = 0; i < img.pixels.length; i++){ 50 | img.pixels[i] = 0xFF000000 | (Data[i] << 16) | (Data[i] << 8) | Data[i]; 51 | } 52 | img.updatePixels(); 53 | return img; 54 | } 55 | }; 56 | 57 | void warp(Image Img, Image Out, int sx, int sy, int ex, int ey, int[] Wx, int[] Wy){ 58 | float a,b,c,d,e,f,i,j,x,y,destX,destY; 59 | 60 | // fix book bug 61 | float sx0 = sx; 62 | float sy0 = sy; 63 | ex = ex - sx; 64 | ey = ey - sy; 65 | sx = 0; 66 | sy = 0; 67 | // end fix 68 | 69 | 70 | a = (float)(-Wx[0]+Wx[1])/(ey-sy); 71 | b = (float)(-Wx[0]+Wx[3])/(ex-sx); 72 | c = (float)(Wx[0]-Wx[1]+Wx[2]-Wx[3])/((ey-sy)*(ex-sx)); 73 | d = (float)Wx[0]; 74 | 75 | e = (float)(-Wy[0]+Wy[1])/(ex-sx); 76 | f = (float)(-Wy[0]+Wy[3])/(ey-sy); 77 | i = (float)(Wy[0]-Wy[1]+Wy[2]-Wy[3])/((ey-sy)*(ex-sx)); 78 | j = (float)Wy[0]; 79 | 80 | 81 | for (y = sy; y < ey; y+=0.5){ 82 | for (x = sx; x < ex; x+=0.5){ 83 | destX = a*x + b*y + c*x*y + d; 84 | destY = e*x + f*y + i*x*y + j; 85 | 86 | //doesn't work: 87 | //Out.Data[(int)(destY)*Out.Cols+(int)destX] = Img.Data[(int)y*Img.Cols+(int)(x)]; 88 | 89 | //does: 90 | Out.Data[(int)(destY)*Out.Cols+(int)destX] = Img.Data[(int)(y+sy0)*Img.Cols+(int)(x+sx0)]; 91 | } 92 | } 93 | 94 | } 95 | 96 | 97 | 98 | void setup(){ 99 | size(1024,512); 100 | PImage img = loadImage("../images/boat.png"); 101 | Image Img = new Image(img); 102 | PImage img0 = Img.toPImage(); 103 | 104 | Image Out = new Image(Img.Cols,Img.Rows); 105 | 106 | int sx = 50; 107 | int sy = 50; 108 | int ex = 300; 109 | int ey = 300; 110 | 111 | int x0 = 20; 112 | int y0 = 100; 113 | 114 | int x1 = 400; 115 | int y1 = 100; 116 | 117 | int x2 = 400; 118 | int y2 = 300; 119 | 120 | int x3 = 100; 121 | int y3 = 400; 122 | 123 | warp(Img,Out,sx,sy,ex,ey, 124 | new int[]{x0,x1,x2,x3}, 125 | new int[]{y0,y1,y2,y3} 126 | ); 127 | 128 | PImage img1 = Out.toPImage(); 129 | image(img0,0,0); 130 | noFill(); 131 | stroke(255,255,0); 132 | rect(sx,sy,ex-sx,ey-sy); 133 | 134 | translate(img0.width,0); 135 | image(img1,0,0); 136 | 137 | circle(x0,y0,6); 138 | circle(x1,y1,6); 139 | circle(x2,y2,6); 140 | circle(x3,y3,6); 141 | 142 | save("preview.png"); 143 | 144 | } 145 | -------------------------------------------------------------------------------- /Warping/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/Warping/preview.png -------------------------------------------------------------------------------- /images/boat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/images/boat.png -------------------------------------------------------------------------------- /images/cameraman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/images/cameraman.png -------------------------------------------------------------------------------- /images/horse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/images/horse.png -------------------------------------------------------------------------------- /images/peppers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms/278c1f7c972137f0ee6ff756d53cd80137df3968/images/peppers.png -------------------------------------------------------------------------------- /thumbnails.py: -------------------------------------------------------------------------------- 1 | from glob import glob 2 | files = sorted(glob("*/preview.png")) 3 | out = "| | |\n|---|---|\n|" 4 | col = 2 5 | l0 = "" 6 | l1 = "" 7 | for i in range(0,len(files)): 8 | 9 | proj = files[i].split("/")[0] 10 | l0 += " [![](./"+files[i]+")](./"+proj+") |" 11 | l1 += " ["+proj+"](./"+proj+") |" 12 | 13 | if ((i+1) % col == 0): 14 | out += l0 + "\n|" + l1 + "\n|" 15 | l0 = "" 16 | l1 = "" 17 | 18 | if (len(l0)): 19 | out += l0 + "\n|" + l1 + "\n" 20 | 21 | print(out) --------------------------------------------------------------------------------