34 #ifdef _MSC_VER // remove annoying warnings in Microsoft Visual C++
35 # pragma warning(disable:4800)
73 pixels(data, data + w * h)
80 ImageBase(img.GetWidth(), img.GetHeight()),
81 pixels(img.begin(), img.end())
88 ImageBase(img.GetWidth(), img.GetHeight()),
89 pixels(img.GetWidth() * img.GetHeight())
91 for (
auto tmp :
Range(img))
92 pixels[tmp] = img.At(tmp) ? std::numeric_limits<T>::max() : T(0);
100 ImageBase(bbox.GetWidth(), bbox.GetHeight()),
101 pixels(bbox.GetWidth() * bbox.GetHeight())
103 for (
int y = 0; y <
height; ++y)
112 ImageBase(bbox.GetWidth(), bbox.GetHeight()),
113 pixels(bbox.GetWidth() * bbox.GetHeight())
116 At(x, y) = img.At(x + bbox.
GetLeft(), y + bbox.
GetTop()) ? std::numeric_limits<T>::max() : T(0);
127 pixels.resize(width * height);
128 std::copy(img.
begin(), img.
end(), pixels.begin());
138 height = img.GetHeight();
139 pixels.resize(width * height);
140 for (
auto tmp :
Range(img))
141 pixels[tmp] = img.At(tmp) ? std::numeric_limits<T>::max() : T(0);
151 pixels.resize(width * height);
152 for (
auto tmp :
Range(img))
153 pixels[tmp] = T(img.
At(tmp));
182 return std::equal(begin(), end(), other.
begin());
198 for (
auto i :
Range(img))
199 At(i) = T(At(i) + img.
At(i));
212 for (
auto i :
Range(img))
213 At(i) = T(At(i) - img.
At(i));
223 for (
auto &v : pixels)
237 for (
auto i :
Range(img))
238 At(i) = T(At(i) * img.
At(i));
251 for (
auto i :
Range(img))
252 At(i) = T(At(i) / img.
At(i));
263 for (
size_t tmp = 0; tmp < width * height; ++tmp)
264 pixels[tmp] = -pixels[tmp];
272 for (
size_t tmp = 0; tmp < width * height; ++tmp)
273 pixels[tmp] = T(maxval - pixels[tmp]);
284 if ((dx >= width) || (dy >= height))
287 auto bbox = srczone & src.
GetBBox();
289 if (dx + bbox.GetWidth() > width)
290 bbox.
SetWidth(
int(width) -
int(dx));
291 if (dy + bbox.GetHeight() > height)
292 bbox.SetWidth(
int(height) -
int(dy));
294 for (
int y = 0; y < bbox.GetHeight(); ++y)
295 std::copy_n(src.
begin() + bbox.GetLeft() + (y + bbox.GetTop()) * src.
GetWidth(), bbox.GetWidth(), pixels.begin() + dx + (dy + y) * width);
313 if ((x >= width) || (y >= height))
315 size_t offset = x + y * width;
319 pixels[offset] = val;
323 if (pixels[offset - 1] == oldval)
324 FloodFill(x - 1, y, val, dist);
326 if (pixels[offset + 1] == oldval)
327 FloodFill(x + 1, y, val, dist);
329 if (pixels[offset - width] == oldval)
330 FloodFill(x, y - 1, val, dist);
332 if (pixels[offset + width] == oldval)
333 FloodFill(x, y + 1, val, dist);
337 if ((x > 0) && (y > 0))
338 if (pixels[offset - 1 - width] == oldval)
339 FloodFill(x - 1, y - 1, val, dist);
340 if ((x > 0) && (y < height - 1))
341 if (pixels[offset - 1 + width] == oldval)
342 FloodFill(x - 1, y + 1, val, dist);
343 if ((x < width - 1) && (y > 0))
344 if (pixels[offset + 1 - width] == oldval)
345 FloodFill(x + 1, y - 1, val, dist);
346 if ((x < width - 1) && (y < height - 1))
347 if (pixels[offset + 1 + width] == oldval)
348 FloodFill(x + 1, y + 1, val, dist);
365 if ((x >= width) || (y >= height))
370 auto todo = std::vector<std::pair<int, std::pair<int, int>>>{};
372 for (bx =
int(x); bx >= 0; --bx)
373 if (pixels[bx + y * width] != oldval)
377 for (ex =
int(x); ex < int(width); ++ex)
378 if (pixels[ex + y * width] != oldval)
381 todo.emplace_back(
int(y), std::make_pair(bx, ex));
382 while (!todo.empty())
384 auto pos(todo.back());
386 auto checklimitup = pos.second.first - 1;
387 auto checklimitdown = pos.second.first - 1;
388 for (
int tx = pos.second.first; tx <= pos.second.second; ++tx)
391 pixels[tx + pos.first * width] = val;
395 if ((tx > checklimitup) && (pixels[tx + (pos.first - 1) * width] == oldval))
398 for (bx = tx; bx >= 0; --bx)
399 if (pixels[bx + (pos.first - 1) * width] != oldval)
402 for (ex = tx; ex < width; ++ex)
403 if (pixels[ex + (pos.first - 1) * width] != oldval)
406 todo.emplace_back(pos.first - 1, std::make_pair(bx, ex));
412 if ((tx == pos.second.first) && (tx > 0) && (tx - 1 >= checklimitup))
414 if (pixels[tx - 1 + (pos.first - 1) * width] == oldval)
417 for (bx = tx - 1; bx >= 0; --bx)
418 if (pixels[bx + (pos.first - 1) * width] != oldval)
421 for (ex = tx - 1; ex < width; ++ex)
422 if (pixels[ex + (pos.first - 1) * width] != oldval)
425 todo.emplace_back(pos.first - 1, std::make_pair(bx, ex));
430 if ((tx == pos.second.second) && (tx < width - 1) && (tx + 1 > checklimitup))
432 if (pixels[tx + 1 + (pos.first - 1) * width] == oldval)
435 for (bx = tx + 1; bx >= 0; --bx)
436 if (pixels[bx + (pos.first - 1) * width] != oldval)
439 for (ex = tx + 1; ex < width; ++ex)
440 if (pixels[ex + (pos.first - 1) * width] != oldval)
443 todo.emplace_back(pos.first - 1, std::make_pair(bx, ex));
450 if ((pos.first < height - 1) && (tx > checklimitdown))
452 if (pixels[tx + (pos.first + 1) * width] == oldval)
455 for (bx = tx; bx >= 0; --bx)
456 if (pixels[bx + (pos.first + 1) * width] != oldval)
459 for (ex = tx; ex < width; ++ex)
460 if (pixels[ex + (pos.first + 1) * width] != oldval)
463 todo.emplace_back(pos.first + 1, std::make_pair(bx, ex));
469 if ((tx == pos.second.first) && (tx > 0) && (tx - 1 >= checklimitdown))
471 if (pixels[tx - 1 + (pos.first + 1) * width] == oldval)
474 for (bx = tx - 1; bx >= 0; --bx)
475 if (pixels[bx + (pos.first + 1) * width] != oldval)
478 for (ex = tx - 1; ex < width; ++ex)
479 if (pixels[ex + (pos.first + 1) * width] != oldval)
482 todo.emplace_back(pos.first + 1, std::make_pair(bx, ex));
487 if ((tx == pos.second.second) && (tx < width - 1) && (tx + 1 > checklimitdown))
489 if (pixels[tx + 1 + (pos.first + 1) * width] == oldval)
492 for (bx = tx + 1; bx >= 0; --bx)
493 if (pixels[bx + (pos.first + 1) * width] != oldval)
496 for (ex = tx + 1; ex < width; ++ex)
497 if (pixels[ex + (pos.first + 1) * width] != oldval)
500 todo.emplace_back(pos.first + 1, std::make_pair(bx, ex));
518 const auto clip = r & GetBBox();
523 std::fill_n(pixels.begin() + clip.GetLeft() + clip.GetTop() * width, clip.GetWidth(), color);
525 std::fill_n(pixels.begin() + clip.GetLeft() + clip.GetBottom() * width, clip.GetWidth(), color);
527 for (
int y = clip.GetTop() + 1; y < clip.GetBottom(); ++y)
530 std::fill_n(pixels.begin() + clip.GetLeft() + y * width, clip.GetWidth(), color);
532 At(clip.GetLeft(), y) = At(clip.GetRight(), y) = color;
546 int lg_delta, sh_delta, cycle, lg_step, sh_step;
547 lg_delta = (int)x2 - (
int)x1;
548 sh_delta = (int)y2 - (
int)y1;
551 lg_delta = -lg_delta;
559 sh_delta = -sh_delta;
565 if (sh_delta < lg_delta)
567 cycle = lg_delta >> 1;
570 if ((x1 >= 0) && (x1 < width) && (y1 >= 0) && (y1 < height))
574 cycle = cycle + sh_delta;
575 if (cycle > lg_delta)
578 cycle = cycle - lg_delta;
584 cycle = sh_delta >> 1;
593 if ((x1 >= 0) && (x1 < width) && (y1 >= 0) && (y1 < height))
597 cycle = cycle + sh_delta;
598 if (cycle > lg_delta)
601 cycle = cycle - lg_delta;
616 const auto defaultvalue = pixels.front();
617 const auto nullvalue = defaultvalue - defaultvalue;
619 auto wscale = std::vector<pixel_type>();
620 auto step = (double)width / (
double)w;
628 wscale = std::vector<pixel_type>(w * height, defaultvalue);
629 for (
auto x =
size_t(0); x < w; ++x)
631 auto min = int(
double(x) * step);
632 auto coeffmin = 1 - (double(x) * step - double(min));
634 auto max = int(
double(x + 1) * step);
635 auto coeffmax = double(x + 1) * step - double(max);
640 coeffmax = 1 - coeffmin;
642 if (max >=
int(width))
644 for (
size_t y = 0; y < height; ++y)
648 auto yoff = y * width;
650 acc += coeffmin * pixels[min + yoff];
654 acc += coeffmax * pixels[max + yoff];
657 for (
int k = min + 1; k < max; ++k)
659 acc += pixels[k + yoff];
663 wscale[x + y * w] = T(acc / coeff);
669 step = (double)height / (
double)h;
677 pixels = std::vector<pixel_type>(w * h, defaultvalue);
679 for (
size_t y = 0; y < h; ++y)
681 auto min = int(
double(y) * step);
682 auto coeffmin = 1 - (double(y) * step - double(min));
684 auto max = int(
double(y + 1) * step);
685 auto coeffmax = double(y + 1) * step - double(max);
690 coeffmax = 1 - coeffmin;
692 if (max >=
int(height))
694 for (
size_t x = 0; x < w; ++x)
699 acc += coeffmin * wscale[x + min * w];
703 acc += coeffmax * wscale[x + max * w];
706 for (
int k = min + 1; k < max; ++k)
708 acc += wscale[x + k * w];
713 pixels[x + y * w] = T(acc / coeff);
733 for (
size_t y = 0; y < height; ++y)
735 std::reverse(pixels.begin() + y * width, pixels.begin() + (y + 1) * width);
740 auto tmp = std::vector<T>(width);
741 for (
size_t y = 0; y < height / 2; ++y)
743 std::copy_n(pixels.begin() + y * width, width, tmp.begin());
744 std::copy_n(pixels.begin() + (height - 1 - y) * width, width, pixels.begin() + y * width);
745 std::copy_n(tmp.begin(), width, pixels.begin() + (height - 1 - y) * width);
764 throw ExceptionDimension(
"void Image::Dilate(const MatrixInt &strel): even matrix dimensions.");
766 auto halfw = int(strel.
GetCols()) / 2;
767 auto halfh = int(strel.
GetRows()) / 2;
769 auto newpix = std::vector<pixel_type>(width * height, pixels.front());
770 for (
size_t y = 0; y < height; ++y)
772 for (
size_t x = 0; x < width; ++x)
774 auto basey = int(y) - halfh;
775 auto basex = int(x) - halfw;
777 auto pix = [&strel, basex, basey,
this]()
779 for (
size_t cy = 0; cy < strel.
GetRows(); cy++)
780 for (
size_t cx = 0; cx < strel.
GetCols(); cx++)
784 auto tx = basex + int(cx);
785 if (tx < 0)
continue;
786 if (tx >=
int(GetWidth()))
continue;
787 auto ty = basey + int(cy);
788 if (ty < 0)
continue;
789 if (ty >=
int(GetHeight()))
continue;
791 return pixels[tx + ty * width];
793 return pixels[basex + basey * width];
795 for (
size_t cy = 0; cy < strel.
GetRows(); ++cy)
796 for (
size_t cx = 0; cx < strel.
GetCols(); ++cx)
800 auto tx = basex + int(cx);
801 if (tx < 0)
continue;
802 if (tx >=
int(GetWidth()))
continue;
803 auto ty = basey + int(cy);
804 if (ty < 0)
continue;
805 if (ty >=
int(GetHeight()))
continue;
808 if (cmp(tmppix, pix))
811 newpix[x + y * width] = pix;
830 throw ExceptionDimension(
"void Image::Erode(MatrixInt &strel): even matrix dimensions.");
832 auto halfw = int(strel.
GetCols()) / 2;
833 auto halfh = int(strel.
GetRows()) / 2;
835 auto newpix = std::vector<pixel_type>(width * height, pixels.front());
836 for (
size_t y = 0; y < height; y++)
837 for (
size_t x = 0; x < width; x++)
839 const auto basey = int(y) - halfh;
840 const auto basex = int(x) - halfw;
842 auto pix = [&strel, basex, basey,
this]()
844 for (
size_t cy = 0; cy < strel.
GetRows(); cy++)
845 for (
size_t cx = 0; cx < strel.
GetCols(); cx++)
849 auto tx = basex + int(cx);
850 if (tx < 0)
continue;
851 if (tx >=
int(GetWidth()))
continue;
852 auto ty = basey + int(cy);
853 if (ty < 0)
continue;
854 if (ty >=
int(GetHeight()))
continue;
856 return pixels[tx + ty * width];
858 return pixels[basex + basey * width];
860 for (
size_t cy = 0; cy < strel.
GetRows(); cy++)
861 for (
size_t cx = 0; cx < strel.
GetCols(); cx++)
865 auto tx = basex + int(cx);
866 if (tx < 0)
continue;
867 if (tx >=
int(GetWidth()))
continue;
868 auto ty = basey + int(cy);
869 if (ty < 0)
continue;
870 if (ty >=
int(GetHeight()))
continue;
873 if (!cmp(tmppix, pix))
876 newpix[x + y * width] = pix;
894 if ((index == 0) && (halfwin < 8))
896 Dilate(
MatrixInt{halfwin + halfwin + 1, halfwin + halfwin + 1, 1}, cmp);
900 auto wins = std::vector<std::multiset<pixel_type, CMP>>(width, std::multiset<pixel_type, CMP>(cmp));
901 for (
size_t x = 0; x < width; ++x)
902 for (
size_t y = 0; y < halfwin; ++y)
903 wins[x].insert(At(x, y));
905 auto newpix = std::vector<pixel_type>(width * height, pixels.front());
906 for (
size_t y = 0; y < height; ++y)
911 const auto ry = y - halfwin - 1;
912 for (
size_t x = 0; x < width; ++x)
914 auto it = wins[x].find(At(x, ry));
915 if (it != wins[x].end())
919 if (y + halfwin < height)
921 const auto ay = y + halfwin;
922 for (
size_t x = 0; x < width; ++x)
923 wins[x].insert(At(x, ay));
926 auto mins = std::vector<pixel_type>(width, pixels.front());
927 for (
size_t x = 0; x < width; ++x)
929 auto cmin = wins[x].begin();
930 std::advance(cmin,
Min(index, wins[x].size() - 1));
935 for (
size_t x = 0; x < width; ++x)
937 const auto bx = (x >= halfwin) ? x - halfwin : 0;
938 const auto ex =
Min(x + halfwin, width - 1);
939 auto minval = mins[bx];
940 for (
auto tx = bx + 1; tx <= ex; ++tx)
941 if (cmp(mins[tx], minval)) minval = mins[tx];
942 newpix[x + y * width] = minval;
956 template<
typename T>
template<
typename CMP>
void Image<T>::FastErode(
size_t halfwin,
size_t index, CMP cmp)
960 if ((index == 0) && (halfwin < 8))
962 Erode(
MatrixInt{halfwin + halfwin + 1, halfwin + halfwin + 1, 1}, cmp);
966 auto wins = std::vector<std::multiset<pixel_type, CMP>>(width, std::multiset<pixel_type, CMP>(cmp));
967 for (
size_t x = 0; x < width; ++x)
968 for (
size_t y = 0; y < halfwin; ++y)
969 wins[x].insert(At(x, y));
971 auto newpix = std::vector<pixel_type>(width * height, pixels.front());
972 for (
size_t y = 0; y < height; ++y)
977 const auto ry = y - halfwin - 1;
978 for (
size_t x = 0; x < width; ++x)
980 auto it = wins[x].find(At(x, ry));
981 if (it != wins[x].end())
985 if (y + halfwin < height)
987 const auto ay = y + halfwin;
988 for (
size_t x = 0; x < width; ++x)
989 wins[x].insert(At(x, ay));
992 auto maxes = std::vector<pixel_type>(width, pixels.front());
993 for (
size_t x = 0; x < width; ++x)
995 auto cmax = wins[x].rbegin();
996 std::advance(cmax,
Min(index, wins[x].size() - 1));
1001 for (
size_t x = 0; x < width; ++x)
1003 const auto bx = (x >= halfwin) ? x - halfwin : 0;
1004 const auto ex =
Min(x + halfwin, width - 1);
1005 auto maxval = maxes[bx];
1006 for (
auto tx = bx + 1; tx <= ex; ++tx)
1007 if (!cmp(maxes[tx], maxval)) maxval = maxes[tx];
1008 newpix[x + y * width] = maxval;
1011 pixels.swap(newpix);
1026 throw ExceptionDimension(
"void Image::Convolve(MatrixDouble &mat): even matrix dimensions.");
1028 auto halfw = mat.
GetCols() / 2;
1029 auto halfh = mat.
GetRows() / 2;
1030 if ((halfh > height) || (halfw > width))
1031 throw ExceptionDomain(
"void Image::Convolve(MatrixDouble &mat): matrix bigger than the image!");
1033 auto oldimg = pixels;
1035 const auto nullvalue = pixels.front() - pixels.front();
1036 for (
size_t y = 0; y < halfh; y++)
1037 for (
size_t x = 0; x < width; x++)
1039 auto basey = int(y) - int(halfh);
1040 auto basex = int(x) - int(halfw);
1042 for (
size_t cy = 0; cy < mat.
GetRows(); cy++)
1043 for (
size_t cx = 0; cx < mat.
GetCols(); cx++)
1045 auto tx = basex + int(cx);
1047 if (tx >=
int(width)) tx = int(width) - 1;
1048 auto ty = basey + int(cy);
1050 if (ty >=
int(height)) ty = int(height) - 1;
1051 sum += oldimg[tx + ty * width] * mat[cy][cx];
1055 for (
int y =
int(height) -
int(halfh); y < int(height); y++)
1056 for (
size_t x = 0; x < width; x++)
1058 auto basey = y - int(halfh);
1059 auto basex = int(x) - int(halfw);
1061 for (
size_t cy = 0; cy < mat.
GetRows(); cy++)
1062 for (
size_t cx = 0; cx < mat.
GetCols(); cx++)
1064 auto tx = basex + int(cx);
1066 if (tx >=
int(width)) tx = int(width) - 1;
1067 auto ty = basey + int(cy);
1069 if (ty >=
int(height)) ty = int(height) - 1;
1070 sum += oldimg[tx + ty * width] * mat[cy][cx];
1074 for (
int y =
int(halfh); y < int(height) - int(halfh); y++)
1075 for (
size_t x = 0; x < halfw; x++)
1077 auto basey = y - int(halfh);
1078 auto basex = int(x) - int(halfw);
1080 for (
size_t cy = 0; cy < mat.
GetRows(); cy++)
1081 for (
size_t cx = 0; cx < mat.
GetCols(); cx++)
1083 auto tx = basex + int(cx);
1085 if (tx >=
int(width)) tx = int(width) - 1;
1086 auto ty = basey + int(cy);
1088 if (ty >=
int(height)) ty = int(height) - 1;
1089 sum += oldimg[tx + ty * width] * mat[cy][cx];
1093 for (
int y =
int(halfh); y < int(height) - int(halfh); y++)
1094 for (
int x =
int(width) - int(halfw); x < int(width); x++)
1096 auto basey = y - int(halfh);
1097 auto basex = x - int(halfw);
1099 for (
size_t cy = 0; cy < mat.
GetRows(); cy++)
1100 for (
size_t cx = 0; cx < mat.
GetCols(); cx++)
1102 auto tx = basex + int(cx);
1104 if (tx >=
int(width)) tx = int(width) - 1;
1105 auto ty = basey + int(cy);
1107 if (ty >=
int(height)) ty = int(height) - 1;
1108 sum += oldimg[tx + ty * width] * mat[cy][cx];
1112 for (
int y =
int(halfh); y < int(height) - int(halfh); y++)
1113 for (
int x =
int(halfw); x < int(width) - int(halfw); x++)
1115 auto basey = y - int(halfh);
1116 auto basex = x - int(halfw);
1118 for (
size_t cy = 0; cy < mat.
GetRows(); cy++)
1119 for (
size_t cx = 0; cx < mat.
GetCols(); cx++)
1121 sum += oldimg[basex + cx + (basey + cy) * width] * mat[cy][cx];
1145 kernel.NormalizeForConvolution();
1147 const auto kernel_size(kernel.GetCols());
1148 const auto radius(kernel_size / 2);
1149 std::vector<double> coeffs(radius + 1);
1150 auto index = size_t(0);
1152 for (
auto k = radius; k < kernel_size; ++k)
1154 coeffs[index] = kernel[0][k];
1161 auto left_bound = size_t(0);
1162 auto right_bound = width - 1;
1164 std::vector<pixel_type> tmp_row(width, pixels.front());
1165 auto back_shifted_index = size_t(0);
1167 for (
auto r = 0; r < height; ++r)
1173 for (
auto c =
size_t(0); c < radius; ++c)
1175 auto weight = coeffs[0];
1176 auto local_cumul = pixels[index] * weight;
1178 auto index_left = int(index);
1179 auto index_right = index_left;
1181 for (
auto offset =
size_t(1); offset <= radius; ++offset)
1184 index_left =
Max(
int(left_bound), index_left);
1186 weight = coeffs[offset];
1188 local_cumul += (pixels[index_left] + pixels[index_right]) * weight;
1196 left_bound += width;
1200 for (
auto c = radius; c < width - radius; ++c)
1202 auto weight = coeffs[0];
1203 auto local_cumul = pixels[index] * weight;
1205 auto index_left = int(index);
1206 auto index_right = index_left;
1208 for (
auto offset =
size_t(1); offset <= radius; ++offset)
1212 weight = coeffs[offset];
1214 local_cumul += (pixels[index_left] + pixels[index_right]) * weight;
1224 for (
auto c = width - radius; c < width; ++c)
1226 auto weight = coeffs[0];
1227 auto local_cumul = pixels[index] * weight;
1229 auto index_left = int(index);
1230 auto index_right = index_left;
1232 for (
auto offset = 1; offset <= radius; ++offset)
1236 index_right =
Min(
int(right_bound),
int(index_right));
1237 weight = coeffs[offset];
1239 local_cumul += (pixels[index_left] + pixels[index_right]) * weight;
1247 right_bound += width;
1251 for (
auto k = 0; k < width; ++k)
1253 pixels[back_shifted_index] = tmp_row[k];
1254 ++back_shifted_index;
1260 std::vector<size_t> indices(kernel_size, 0);
1261 std::vector<pixel_type> tmp_col(height, pixels.front());
1264 for (
auto c = 0; c < width; ++c)
1270 for (
auto k =
size_t(0); k <= radius; ++k)
1273 for (
auto k = radius + 1; k < kernel_size; ++k)
1274 indices[k] = indices[k - 1] + width;
1276 for (
auto r =
size_t(0); r < radius; ++r)
1279 auto local_cumul = pixels[indices[radius]] * weight;
1281 for (
auto offset = 1; offset <= radius; ++offset)
1283 auto id_upper = indices[radius - offset];
1284 auto id_lower = indices[radius + offset];
1286 weight = coeffs[offset];
1287 local_cumul += (pixels[id_upper] + pixels[id_lower]) * weight;
1293 for (
auto k = 0; k < kernel_size - 1; ++k)
1294 indices[k] = indices[k + 1];
1296 indices[kernel_size - 1] += width;
1302 for (
auto r = radius; r < height - radius; ++r)
1305 auto local_cumul = pixels[indices[radius]] * weight;
1307 for (
auto offset =
size_t(1); offset <= radius; ++offset)
1309 auto id_upper = indices[radius - offset];
1310 auto id_lower = indices[radius + offset];
1312 weight = coeffs[offset];
1313 local_cumul += (pixels[id_upper] + pixels[id_lower]) * weight;
1319 for (
auto k = 0; k < kernel_size - 1; ++k)
1320 indices[k] = indices[k + 1];
1322 indices[kernel_size - 1] += width;
1327 indices[kernel_size - 1] = c + (height - 1) * width;
1329 for (
auto r = height - radius; r < height; ++r)
1332 auto local_cumul = pixels[indices[radius]] * weight;
1334 for (
auto offset =
size_t(1); offset <= radius; ++offset)
1336 auto id_upper = indices[radius - offset];
1337 auto id_lower = indices[radius + offset];
1339 weight = coeffs[offset];
1340 local_cumul += (pixels[id_upper] + pixels[id_lower]) * weight;
1346 for (
auto k =
size_t(0); k < kernel_size - 1; ++k)
1347 indices[k] = indices[k + 1];
1354 for (
auto k =
size_t(0); k < height; ++k)
1356 pixels[index] = tmp_col[k];
1370 res.At(i) = R(i1.
At(i)) + R(i2.
At(i));
1381 res.At(i) = R(i1.
At(i)) - R(i2.
At(i));
1392 res.At(i) = R(i1.
At(i)) * R(i2.
At(i));
1401 res.At(tmp) = R(i.
At(tmp)) * R(d);
1416 for (
auto i :
Range(i1))
1417 res.At(i) = R(i1.
At(i)) / R(i2.
At(i));
1433 auto values = std::set<T>{};
1434 for (
const auto &p : img)
1437 if (values.size() > 2)
1447 template<
typename T,
typename CMP> std::pair<T, T>
MinMax(
const Image<T> &img, CMP cmp)
1449 auto mM = std::minmax_element(img.
begin(), img.
end(), cmp);
1450 return std::make_pair(*mM.first, *mM.second);
1463 for (
int x = bbox.GetLeft(); x <= bbox.GetRight(); ++x)
1466 for (
int y = bbox.GetTop(); y <= bbox.GetBottom(); ++y)
1467 if (img.
At(x, y) != bgval)
1479 for (
int x = bbox.GetRight(); x >= bbox.GetLeft(); --x)
1482 for (
int y = bbox.GetTop(); y <= bbox.GetBottom(); ++y)
1483 if (img.
At(x, y) != bgval)
1495 for (
int y = bbox.GetTop(); y <= bbox.GetBottom(); ++y)
1498 for (
int x = bbox.GetLeft(); x <= bbox.GetRight(); ++x)
1499 if (img.
At(x, y) != bgval)
1511 for (
int y = bbox.GetBottom(); y >= bbox.GetTop(); --y)
1514 for (
int x = bbox.GetLeft(); x <= bbox.GetRight(); ++x)
1515 if (img.
At(x, y) != bgval)
1555 double logs = log2(
int(w));
1556 while (logs != ceil(logs))
1559 logs = log2(
int(w));
1562 logs = log2(
int(h));
1563 while (logs != ceil(logs))
1566 logs = log2(
int(h));
1571 for (
size_t r = 0; r < img1.
GetHeight(); ++r)
1572 for (
size_t c = 0; c < img1.
GetWidth(); ++c)
1573 c1[r][c] = std::complex<double>(img1.
At(c, r));
1576 for (
size_t r = 0; r < img2.
GetHeight(); ++r)
1577 for (
size_t c = 0; c < img2.
GetWidth(); ++c)
1578 c2[r][c] = std::complex<double>(img2.
At(c, r));
1580 for (
size_t r = 0; r < h; ++r)
1581 for (
size_t c = 0; c < w; ++c)
1582 c1[r][c] *= std::conj(c2[r][c]);
1588 for (
size_t r = 0; r < h; ++r)
1589 for (
size_t c = 0; c < w; ++c)
1591 double corr = std::norm(c1[r][c]);
1602 p.
X = (p.
X % hw) - hw;
1604 p.
Y = (p.
Y % hh) - hh;
1620 for (
size_t x = 1; x < img.
GetWidth(); ++x)
1621 sum.SetValue(x, 0, sum.GetValue(x - 1, 0) +
SumType(img.
At(x)));
1623 for (
size_t y = 1; y < img.
GetHeight(); ++y)
1624 sum.SetValue(0, y, sum.GetValue(0, y - 1) +
SumType(img.
At(0, y)));
1626 for (
size_t y = 1; y < img.
GetHeight(); ++y)
1627 for (
size_t x = 1; x < img.
GetWidth(); ++x)
1628 sum.SetValue(x, y,
SumType(img.
At(x, y)) + sum.GetValue(x - 1, y) + sum.GetValue(x, y - 1) - sum.GetValue(x - 1, y - 1));
1644 size_t rowoffset = row * dest.
GetWidth();
1647 for (
int x = 0; x < offset; ++x)
1648 dest.
At(x + rowoffset) = bgColor;
1651 auto srcrowoffset = row * src.
GetWidth();
1652 for (
size_t x = 0; x < src.
GetWidth(); ++x)
1654 T pix = src.
At(x + srcrowoffset);
1662 if ((x + offset >= 0) && (x + offset < dest.
GetWidth()))
1664 dest.
At(x + offset + rowoffset) = pix;
1667 oldPrevPix = prevPix;
1670 size_t x = src.
GetWidth() + offset;
1679 dest.
At(x + rowoffset) = pix;
1681 for (++x; x < dest.
GetWidth(); ++x)
1683 dest.
At(x + rowoffset) = bgColor;
1698 for (
int y = 0; y < offset; ++y)
1700 dest.
At(col, y) = bgColor;
1704 for (
size_t y = 0; y < src.
GetHeight(); ++y)
1706 T pix = src.
At(col, y);
1714 if ((y + offset >= 0) && (y + offset < dest.
GetHeight()))
1716 dest.
At(col, y + offset) = pix;
1719 oldPrevPix = prevPix;
1731 dest.
At(col, y) = pix;
1735 dest.
At(col, y) = bgColor;
1746 if (angle.
value == 0.0)
1750 const auto radAngle = angle.
Get<
Radian>();
1751 const auto rotCos = cos(radAngle);
1752 const auto rotSin = sin(radAngle);
1753 const auto rotTan = tan(radAngle / 2.0);
1757 for (
size_t y = 0; y < shear1.GetHeight(); ++y)
1762 shear = (double(y) + 0.5) * rotTan;
1766 shear = (double(y) - double(shear1.GetHeight()) + 0.5) * rotTan;
1768 ShiftCopyRow(shear1, img, y,
int(floor(shear)), shear - floor(shear), bgColor);
1776 shear = (double(img.
GetWidth()) - 1.0) * rotSin;
1780 shear = double(shear1.GetWidth() - img.
GetWidth()) * rotSin;
1782 for (
size_t x = 0; x < shear2.GetWidth(); ++x, shear -= rotSin)
1784 ShiftCopyColumn(shear2, shear1, x,
int(floor(shear)), shear - floor(shear), bgColor);
1792 shear = (1.0 - double(img.
GetWidth())) * rotSin * rotTan;
1796 shear = ((1.0 - double(img.
GetWidth())) * rotSin + 1.0 - double(shear3.GetHeight())) * rotTan;
1798 for (
size_t y = 0; y < shear3.GetHeight(); ++y, shear += rotTan)
1800 ShiftCopyRow(shear3, shear2, y,
int(floor(shear)), shear - floor(shear), bgColor);
1816 while (rot.value >= 360.0)
1818 while (rot.value < 0.0)
1822 else if (rot.value == 90)
1824 else if (rot.value == 180)
1826 else if (rot.value == 270)
1831 if (rot.value > 225.0)
1837 else if (rot.value > 135.0)
1843 else if (rot.value > 45.0)
1861 newi.At(y, img.
GetWidth() - 1 - x) = img.
At(x, y);
1872 const auto size = img.
Size();
1873 for (
size_t tmp = 0; tmp < size; ++tmp)
1874 newi.At(size - tmp) = img.
At(tmp);
1886 newi.At(img.
GetHeight() - 1 - y, x) = img.
At(x, y);
void ShiftCopyRow(Image< T > &dest, const Image< T > &src, size_t row, int offset, double prevWeight, const T &bgColor)
Copies a row with a shift.
Image< T > MakeSmallRotation(const Image< T > &img, const Angle< Degree > &angle, const T &bgColor)
typename TypeInfo< T >::SumType SumType
void GaussianBlur(double sigma)
Gaussian blur.
Abstract class for images.
void Dilate(const MatrixInt &strel, CMP cmp=std::less< pixel_type >{})
Morphological dilatation.
crn::Image< crn::SumType< typename std::common_type< T1, T2 >::type > > operator*(const crn::Image< T1 > &i1, const crn::Image< T2 > &i2)
ScalarRange< T > Range(T b, T e)
Creates a range [[b, e[[.
size_t GetRows() const noexcept
Returns the number of rows.
size_t GetCols() const noexcept
Returns the number of columns.
void ShiftCopyColumn(Image< T > &dest, const Image< T > &src, size_t col, int offset, double prevWeight, const T &bgColor)
Copies a column with a shift.
void ScanFill(size_t x, size_t y, const pixel_type &val, crn::DistanceType dist=crn::DistanceType::D4)
Fills a portion of the image.
Orientation
An enumeration of orientations.
void Assign(const Image< Y > &img)
Force copy operator (pixel cast)
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
Image< T > MakeRotation(const Image< T > &img, const Angle< Degree > &angle, const T &bgColor)
Creates a rotated version of the image.
size_t GetHeight() const noexcept
std::vector< pixel_type > pixels
static MatrixDouble NewGaussianLine(double sigma)
Creates a line matrix with a centered Gaussian.
const T & Max(const T &a, const T &b)
Returns the max of two values.
Rect AutoCrop(const Image< T > &img, const T &bgval)
Estimates the ideal crop for the image.
void Flip(const Orientation &ori)
Flips the image.
void SavePNG(const ImageBW &img, const Path &fname)
Saves as PNG file.
crn::Image< crn::SumType< typename std::common_type< T1, T2 >::type > > operator/(const crn::Image< T1 > &i1, const crn::Image< T2 > &i2)
std::vector< pixel_type >::iterator end()
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
int GetTop() const
Returns the topmost coordinate.
int GetLeft() const
Returns the leftmost coordinate.
void Erode(const MatrixInt &strel, CMP cmp=std::less< pixel_type >{})
Morphological erosion.
A convenience class for angles units.
Image & operator*=(double f)
Multiplies all pixels.
crn::Image< crn::DiffType< typename std::common_type< T1, T2 >::type > > operator-(const crn::Image< T1 > &i1, const crn::Image< T2 > &i2)
Image< T > Make270Rotation(const Image< T > &img)
Creates a rotated version of the image.
void FastDilate(size_t halfwin, size_t index=0, CMP cmp=std::less< pixel_type >{})
Morphological dilatation.
int SetWidth(int wid)
Changes the width of the rectangle.
Image & operator/=(const Image &img)
Divides by another image's pixels.
Image & operator=(const Image &img)=default
Copy operator.
void SetValue(size_t x, size_t y, T val)
Alters the value of a bin.
A convenience class for file paths.
U::type Get() const noexcept(std::is_nothrow_constructible< typename U::type >::value &&std::is_nothrow_constructible< typename TypeInfo< typename Unit::type >::SumType >::value)
Conversion to any other angle unit.
bool IsBitonal(const Image< T > &img)
Is the image binary (black & white)?
Image & operator+=(const Image &img)
Adds another image.
SummedAreaTable< SumType< T > > MakeSummedAreaTable(const Image< T > &img)
Creates a summed area table of the image.
crn::Image< crn::SumType< typename std::common_type< T1, T2 >::type > > operator+(const crn::Image< T1 > &i1, const crn::Image< T2 > &i2)
Image & operator-=(const Image &img)
Subtracts another image.
Image< T > Make90Rotation(const Image< T > &img)
Creates a rotated version of the image.
Point2DInt CrossCorrelation(const Image< T > &img1, const Image< Y > &img2, T fill1=T(0), Y fill2=Y(0))
Best match between two images.
virtual void SavePNG(const Path &fname) const override
Saves as PNG file.
void Convolve(const MatrixDouble &mat)
Convolution.
typename TypeInfo< T >::DecimalType DecimalType
void DrawLine(size_t x1, size_t y1, size_t x2, size_t y2, pixel_type color)
Draws a line using a specified color.
virtual void SaveJPEG(const Path &fname, unsigned int qual) const override
Saves as JPEG file.
void Abs(Image< T > &img, typename std::enable_if< std::is_arithmetic< T >::value >::type *dummy=nullptr) noexcept
Replaces each pixel by its absolute value.
typename TypeInfo< T >::DiffType DiffType
DistanceType
An enumeration of distances.
std::vector< pixel_type >::iterator begin()
const T & Min(const T &a, const T &b)
Returns the min of two values.
Image()
Default constructor.
Rect GetBBox() const noexcept
void Complement(pixel_type maxval=std::numeric_limits< pixel_type >::max())
Complement.
void Blit(const Image< Y > &src, const Rect &srczone, size_t dx, size_t dy)
Copies a part of an image.
std::pair< T, T > MinMax(const Image< T > &img, CMP cmp=CMP{})
Returns min and max pixel values.
void SaveJPEG(const ImageBW &img, const Path &fname, unsigned int qual)
Saves as JPEG file.
size_t GetWidth() const noexcept
size_t Size() const noexcept
Image< T > MakeAutoCrop(const Image< T > &img, const T &bgval)
Creates a new image as the ideal crop for the image.
Image< T > Make180Rotation(const Image< T > &img)
Creates a rotated version of the image.
void DrawRect(const Rect &r, pixel_type color, bool filled=false)
Draws a rectangle using a specified color.
virtual void ScaleToSize(size_t w, size_t h) override
Scales the image.
bool operator==(const Image &other) const
Tests equality.
void FastErode(size_t halfwin, size_t index=0, CMP cmp=std::less< pixel_type >{})
Morphological erosion.
Invalid argument error (e.g.: nullptr pointer)
void FloodFill(size_t x, size_t y, const pixel_type &val, crn::DistanceType dist=crn::DistanceType::D4)
Flood fills a portion of the image.
int SetLeft(int begX) noexcept
Changes the leftmost coordinate.