1 /* 2 Copyright (c) 2019- Ferhat Kurtulmuş 3 Boost Software License - Version 1.0 - August 17th, 2003 4 Permission is hereby granted, free of charge, to any person or organization 5 obtaining a copy of the software and accompanying documentation covered by 6 this license (the "Software") to use, reproduce, display, distribute, 7 execute, and transmit the Software, and to prepare derivative works of the 8 Software, and to permit third-parties to whom the Software is furnished to 9 do so, all subject to the following: 10 The copyright notices in the Software and this entire statement, including 11 the above license grant, this restriction and the following disclaimer, 12 must be included in all copies of the Software, in whole or in part, and 13 all derivative works of the Software, unless such copies or derivative 14 works are solely in the form of machine-executable object code generated by 15 a source language processor. 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 19 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 20 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 DEALINGS IN THE SOFTWARE. 23 */ 24 25 module measure.types; 26 27 import std.math; 28 import std.algorithm; 29 import std.algorithm.searching; 30 31 struct XYList { 32 // this is useful for many situation instead of using Point[] 33 int[] xs; 34 int[] ys; 35 } 36 37 struct Rectangle{ 38 int x; 39 int y; 40 int width; 41 int height; 42 } 43 44 struct Ellipse{ 45 double angle; 46 double center_x; 47 double center_y; 48 double maj; 49 double min; 50 } 51 52 struct Point { 53 int x; 54 int y; 55 56 int opCmp(Point rhs) { 57 if (x < rhs.x) return -1; 58 if (rhs.x < x) return 1; 59 return 0; 60 } 61 62 void toString(scope void delegate(const(char)[]) sink) const { 63 import std.format; 64 sink("("); 65 formattedWrite(sink, "%d", x); 66 sink(","); 67 formattedWrite(sink, "%d", y); 68 sink(")"); 69 } 70 } 71 72 class Region{ 73 Mat2D!ubyte image; 74 75 // spatial raw moments 76 double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; 77 78 ulong area; 79 double areaFromContour; 80 double perimeter; 81 Point centroid; 82 double aspect_Ratio; 83 Rectangle bBox; 84 XYList convexHull; 85 double convexArea; 86 Ellipse ellipse; 87 double extent; 88 double solidity; 89 double majorAxisLength; 90 double minorAxisLength; 91 double orientation; 92 double eccentricity; 93 double equivalentDiameter; 94 XYList contourPixelList; // chain sorted! 95 XYList pixelList; 96 97 this(){} 98 } 99 100 struct Mat2D(T) 101 { 102 T[] data; 103 size_t rows; 104 size_t cols; 105 106 alias height = rows; 107 alias width = cols; 108 109 this(T)(T[] array, size_t r, size_t c) 110 { 111 data = array; 112 rows = r; 113 cols = c; 114 115 assert (data.length == rows* cols, 116 "array length does not match with rows * cols!"); 117 } 118 119 this(size_t r, size_t c) 120 { 121 data = new T[r*c]; 122 rows = r; 123 cols = c; 124 data[0..$] = 0; 125 } 126 127 this(size_t n){ 128 data = new T[n*n]; 129 rows = n; 130 cols = n; 131 data[0..$] = 0; 132 } 133 134 T opIndex( size_t i, size_t j) 135 { 136 assert((i < rows) && (j < cols), "index out of bounds!"); 137 return data[i * cols + j]; 138 } 139 140 void opIndexAssign(T val, size_t i, size_t j) 141 { 142 assert((i < rows) && (j < cols), "index out of bounds!"); 143 data[i * cols + j] = val; 144 } 145 146 bool opEquals(Mat2D!T other) 147 { 148 return data == other.data; 149 } 150 151 T max() 152 { 153 return data.maxElement; 154 } 155 156 T min() 157 { 158 return data.minElement; 159 } 160 161 int[2] argMax() 162 { 163 const size_t flatInd = maxIndex(data); 164 int yy = cast(int)(flatInd % cols); 165 int xx = cast(int)(flatInd / cols); 166 return [xx, yy]; 167 } 168 int[2] argMin() 169 { 170 const size_t flatInd = minIndex(data); 171 int yy = cast(int)(flatInd % cols); 172 int xx = cast(int)(flatInd / cols); 173 return [xx, yy]; 174 } 175 176 Mat2D!T opMul(S)(Mat2D!S b) 177 { 178 const size_t r1 = rows; 179 const size_t c1 = cols; 180 181 const size_t r2 = b.rows; 182 const size_t c2 = b.cols; 183 184 assert ((c1 == r2), "column of first matrix in not equal to row of second matrix"); 185 186 Mat2D!T mult = Mat2D!T(r1, c2); 187 188 foreach(i; 0..r1) 189 foreach(j; 0..c2){ 190 T tempSum = 0; 191 foreach(k; 0..c1) 192 tempSum += this[i, k] * b[k, j]; 193 mult[i, j] = tempSum; 194 } 195 196 197 return mult; 198 } 199 200 Mat2D!T opMul(T scalar) 201 { 202 Mat2D!T mult = Mat2D!T(rows, cols); 203 foreach(i, val; data) mult.data[i] = cast(T)(val * scalar); 204 return mult; 205 } 206 207 Mat2D!T opMulAssign (Mat2D!T mat) 208 { 209 this = this * mat; 210 return this; 211 } 212 213 Mat2D!T opMulAssign(T scalar) 214 { 215 this = this * scalar; 216 return this; 217 } 218 219 static Mat2D!T diagFromArray(T)(T[] v){ 220 size_t len = v.length; 221 Mat2D!T d = Mat2D!T(len, len); 222 223 foreach(y; 0..len) 224 foreach(x; 0..len){ 225 d[y, x] = 0; 226 if (y == x){ 227 d[y, x] = v[x]; 228 } 229 else 230 d[y, x] = 0; 231 } 232 233 return d; 234 } 235 236 T[] diag() 237 { 238 assert(rows == cols, "The matrix is not square! Diagonal cannot be created!"); 239 240 T[] d; d.length = cols; 241 242 foreach(y; 0..rows) 243 foreach(x; 0..rows){ 244 if (y == x) d[x] = this[y, x]; 245 } 246 return d; 247 } 248 249 Mat2D!T transpose() 250 { 251 252 Mat2D!T transp = Mat2D!T(cols, rows); 253 254 foreach(i; 0..rows) 255 foreach(j; 0..cols) 256 transp[j, i] = this[i, j]; 257 return transp; 258 } 259 260 Mat2D!double inverse() 261 { 262 // https://www.geeksforgeeks.org/adjoint-inverse-matrix/ 263 264 assert(rows == cols, "The matrix is not square, cannot inverse!"); 265 266 size_t N = rows; 267 Mat2D!double inv = Mat2D!double(rows, rows); 268 269 const double det = determinant(this, N); 270 assert (det != 0); // Singular matrix, can't find its inverse 271 272 // Find adjoint 273 Mat2D!double adj = Mat2D!double(rows, rows); 274 adjoint(this, adj); 275 276 for (int i=0; i<N; i++) 277 for (int j=0; j<N; j++) 278 inv[i, j] = adj[i, j]/cast(double)det; 279 280 return inv; 281 } 282 } 283 284 /+ planned for performance, but it badly affects the speed for some reason? 285 struct ROIViewOfMat2D(T) 286 { 287 T* data; 288 size_t rows; 289 size_t cols; 290 291 alias height = rows; 292 alias width = cols; 293 294 Rectangle roi; 295 296 this(T)(Mat2D!T parentMat, Rectangle _roi) 297 { 298 data = parentMat.data.ptr; 299 rows = parentMat.rows; 300 cols = parentMat.cols; 301 roi = _roi; 302 } 303 304 T opIndex( size_t _i, size_t _j) 305 { 306 size_t i = _i + roi.y; 307 size_t j = _j + roi.x; 308 309 assert((i < rows) && (j < cols), "index out of bounds!"); 310 return data[i * cols + j]; 311 } 312 } 313 +/ 314 315 void getCofactor(T)(Mat2D!T A, Mat2D!double temp, int p, int q, ulong n) { 316 int i = 0, j = 0; 317 318 for (int row = 0; row < n; row++) 319 { 320 for (int col = 0; col < n; col++) 321 { 322 if (row != p && col != q) 323 { 324 temp[i, j++] = A[row, col]; 325 326 if (j == n - 1) 327 { 328 j = 0; 329 i++; 330 } 331 } 332 } 333 } 334 } 335 336 double determinant(T)(Mat2D!T A, ulong n) 337 { 338 size_t N = A.rows; 339 double D = 0; 340 341 if (n == 1) 342 return A[0, 0]; 343 344 Mat2D!double temp = Mat2D!double(N, N); 345 346 int sign = 1; 347 348 for (int f = 0; f < n; f++) 349 { 350 getCofactor(A, temp, 0, f, n); 351 D += sign * A[0, f] * determinant(temp, n - 1); 352 sign = -sign; 353 } 354 355 return D; 356 } 357 358 void adjoint(T)(Mat2D!T A, Mat2D!double adj) 359 { 360 size_t N = A.rows; 361 362 if (N == 1) 363 { 364 adj[0, 0] = 1; 365 return; 366 } 367 368 int sign = 1; 369 Mat2D!double temp = Mat2D!double(N, N); 370 371 for (int i=0; i<N; i++) 372 { 373 for (int j=0; j<N; j++) 374 { 375 getCofactor(A, temp, i, j, N); 376 sign = ((i+j)%2==0)? 1: -1; 377 adj[j, i] = (sign)*(determinant(temp, N-1)); 378 } 379 } 380 } 381 382 unittest 383 { 384 double[] aa = [1,2,3,4,5,6]; 385 Mat2D!double maa = Mat2D!double(aa, 2, 3); 386 387 double[] bb = [7,8,9,10,11,12]; 388 Mat2D!double mbb = Mat2D!double(bb, 3, 2); 389 390 auto mul = maa*mbb; 391 assert(maa*mbb == Mat2D!double([58.0, 64.0, 139.0, 154.0], 2, 2)); 392 393 Mat2D!int cc = Mat2D!int([1,2,3,0,1,5,5,6,0], 3, 3); 394 assert(cc.inverse().data == [-6.0, 3.6, 1.4, 5.0, -3.0, -1.0, -1.0, 0.8, 0.2] ); 395 }