.. _program_listing_file__tmp_ws_src_vitis_common_include_imgproc_xf_clahe.hpp: Program Listing for File xf_clahe.hpp ===================================== |exhale_lsh| :ref:`Return to documentation for file ` (``/tmp/ws/src/vitis_common/include/imgproc/xf_clahe.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* * Copyright 2020 Xilinx, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _XF_CLAHE_HPP_ #define _XF_CLAHE_HPP_ #include "ap_int.h" #include "../common/xf_common.hpp" #include "../common/xf_utility.hpp" #include "algorithm" #include "hls_math.h" #include "hls_stream.h" #include "xf_duplicateimage.hpp" #include namespace xf { namespace cv { namespace clahe { template class CLAHETile { public: static constexpr int TILE_HEIGHT_MAX = (HEIGHT % TILES_Y_MIN == 0) ? HEIGHT / TILES_Y_MIN : (HEIGHT + TILES_Y_MIN) / TILES_Y_MIN; static constexpr int TILE_WIDTH_MAX = (WIDTH % TILES_X_MIN == 0) ? WIDTH / TILES_X_MIN : (WIDTH + TILES_X_MIN) / TILES_X_MIN; static constexpr int CLIP_COUNTER_BITS = xf::cv::log2::cvalue; int mRows; int mCols; int mColsNPCAlligned; int mTilesY; int mTilesX; int mTileHeight; int mTileWidth; int mTileWidthNPCAlligned; ap_uint mClipValue; CLAHETile(int height, int width, int clip, int tilesY, int tilesX) : mRows(height), mCols(width), mTilesY(tilesY), mTilesX(tilesX) { #ifndef __SYNTHESIS__ assert((XF_CHANNELS(IN_TYPE, NPC) == 1)); assert(mTilesY >= TILES_Y_MIN); assert(mTilesX >= TILES_X_MIN); assert(mTilesY <= TILES_Y_MAX); assert(mTilesX <= TILES_X_MAX); assert(height <= HEIGHT); assert(width <= WIDTH); #endif mColsNPCAlligned = mCols >> XF_BITSHIFT(NPC); mTileHeight = height / mTilesY; mTileWidth = width / mTilesX; if ((mTileHeight * mTilesY) < height) mTileHeight++; if ((mTileWidth * mTilesX) < width) mTileWidth++; // Make tile width exact multiple of NPC mTileWidthNPCAlligned = (mTileWidth >> XF_BITSHIFT(NPC)) << XF_BITSHIFT(NPC); mClipValue = std::numeric_limits::max(); if (clip > 0) { mClipValue = std::max(((clip * mTileHeight * mTileWidth) >> XF_DTPIXELDEPTH(IN_TYPE, NPC)), 1); } } }; #define _CLAHE_TILE \ CLAHETile template class CLAHEImpl { public: static constexpr int COLS_NPC_ALIGNED = (WIDTH + NPC - 1) >> XF_BITSHIFT(NPC); static constexpr int SATURATION_CAST = (1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)); static constexpr int TILE_HEIGHT_MAX = _CLAHE_TILE::TILE_HEIGHT_MAX; static constexpr int TILE_WIDTH_MAX = _CLAHE_TILE::TILE_WIDTH_MAX; static constexpr int CLIP_COUNTER_BITS = _CLAHE_TILE::CLIP_COUNTER_BITS; static constexpr int _MAXCLIPVALUE = ((CLIPLIMIT * TILE_HEIGHT_MAX * TILE_WIDTH_MAX) >> XF_DTPIXELDEPTH(IN_TYPE, NPC)); static constexpr int MAXCLIPVALUE = (_MAXCLIPVALUE > 1) ? _MAXCLIPVALUE : 1; static constexpr int HIST_COUNTER_BITS = (XF_DTPIXELDEPTH(IN_TYPE, NPC) > xf::cv::log2::cvalue) ? XF_DTPIXELDEPTH(IN_TYPE, NPC) : xf::cv::log2::cvalue; void init(ap_uint _lut[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _clipCounter[TILES_Y_MAX][TILES_X_MAX]) { // clang-format off #pragma HLS inline off // clang-format on INITLY: for (int i = 0; i < TILES_Y_MAX; i++) { INITLX: for (int j = 0; j < TILES_X_MAX; j++) { INITLP: for (int k = 0; k < (1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)); k++) { // clang-format off #pragma HLS PIPELINE // clang-format on for (int l = 0; l < (XF_NPIXPERCYCLE(NPC) << 1); l++) { // clang-format off #pragma HLS UNROLL // clang-format on _lut[i][j][l][k] = 0; } _clipCounter[i][j] = 0; } } } } void clipLut(_CLAHE_TILE& Tile, ap_uint _lut[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _clipCounter[TILES_Y_MAX][TILES_X_MAX]) { // clang-format off #pragma HLS inline off // clang-format on ACY: for (int i = 0; i < TILES_Y_MAX; i++) { ACX: for (int j = 0; j < TILES_X_MAX; j++) { ap_uint add = _clipCounter[i][j]; ACN: for (int k = 0; k < (1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)); k++) { // clang-format off #pragma HLS PIPELINE // clang-format on ap_uint v = 0; for (int l = 0; l < (XF_NPIXPERCYCLE(NPC) << 1); l++) { // clang-format off #pragma HLS UNROLL // clang-format on v += _lut[i][j][l][k]; } if (v > Tile.mClipValue) add += (v - Tile.mClipValue); } int sum = 0; ACK: for (int k = 0; k < (1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)); k++) { // clang-format off #pragma HLS PIPELINE // clang-format on ap_uint div = add >> XF_DTPIXELDEPTH(IN_TYPE, NPC); ap_uint rem = add.range(XF_DTPIXELDEPTH(IN_TYPE, NPC) - 1, 0); ap_uint v = 0; for (int l = 0; l < (XF_NPIXPERCYCLE(NPC) << 1); l++) { // clang-format off #pragma HLS UNROLL // clang-format on v += _lut[i][j][l][k]; } if (v > Tile.mClipValue) v = Tile.mClipValue; v += div; if (rem > 0) { int step = (1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)) / rem; if (step < 1) step = 1; if (((k % step) == 0) && (k < (rem * step))) v++; } sum += v; int tileSize = Tile.mTileHeight * Tile.mTileWidth; int fi = ((sum << XF_DTPIXELDEPTH(IN_TYPE, NPC)) - sum) / tileSize; if (fi > (SATURATION_CAST - 1)) fi = (SATURATION_CAST - 1); for (int l = 0; l < (XF_NPIXPERCYCLE(NPC) << 1); l++) { // clang-format off #pragma HLS UNROLL // clang-format on _lut[i][j][l][k] = fi; } } } } } void populateLutBlk(xf::cv::Mat& in, _CLAHE_TILE& Tile, xf::cv::Mat& in_copy, ap_uint _lut[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _clipCounter[TILES_Y_MAX][TILES_X_MAX]) { // clang-format off #pragma HLS inline off // clang-format on static constexpr int COL_TRIPCOUNT = COLS_NPC_ALIGNED >> 1; int address = 0; int counterY = 0; int histY = 0; ap_uint a_prev; int accum; R: for (int i = 0; i < Tile.mRows; i++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=HEIGHT // clang-format on int counterX = 0; int histX = 0; C: for (int j = 0; j < Tile.mColsNPCAlligned; j += 2) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=COL_TRIPCOUNT #pragma HLS LOOP_FLATTEN OFF #pragma HLS PIPELINE II=2 // clang-format on XF_TNAME(IN_TYPE, NPC) pxl1; XF_TNAME(IN_TYPE, NPC) pxl2; pxl1 = in.read(address); in_copy.write(address, pxl1); if (j < (Tile.mColsNPCAlligned - 1)) { pxl2 = in.read(address + 1); in_copy.write(address + 1, pxl2); } else pxl2 = 0; address += 2; ap_uint _clip1 = 0; ap_uint _clip2 = 0; N: for (int k = 0; k < XF_NPIXPERCYCLE(NPC); k++) { // clang-format off #pragma HLS UNROLL // clang-format on ap_uint a1; ap_uint a2; a1 = pxl1.range(((k + 1) * XF_DTPIXELDEPTH(IN_TYPE, NPC)) - 1, (k * XF_DTPIXELDEPTH(IN_TYPE, NPC))); a2 = pxl2.range(((k + 1) * XF_DTPIXELDEPTH(IN_TYPE, NPC)) - 1, (k * XF_DTPIXELDEPTH(IN_TYPE, NPC))); ap_uint val1 = _lut[histY][histX][2 * k][a1] + 1; ap_uint val2 = _lut[histY][histX][2 * k + 1][a2] + 1; if (val1 <= Tile.mClipValue) _lut[histY][histX][2 * k][a1] = val1; else _clip1++; if (val2 <= Tile.mClipValue) _lut[histY][histX][2 * k + 1][a2] = val2; else _clip2++; } _clipCounter[histY][histX] += (_clip1 + _clip2); counterX += (2 << XF_BITSHIFT(NPC)); if (counterX >= Tile.mTileWidthNPCAlligned) { histX++; counterX = 0; } } counterY++; if (counterY == (Tile.mTileHeight)) { histY++; counterY = 0; } } } void populateLut(xf::cv::Mat& in, _CLAHE_TILE& Tile, xf::cv::Mat& in_copy, ap_uint _lut[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _clipCounter[TILES_Y_MAX][TILES_X_MAX]) { // clang-format off #pragma HLS inline off // clang-format on int address = 0; // Initialize LUT init(_lut, _clipCounter); // Populate LUT populateLutBlk(in, Tile, in_copy, _lut, _clipCounter); // Accumulate values clipLut(Tile, _lut, _clipCounter); } void interpolate(xf::cv::Mat& in, _CLAHE_TILE& Tile, ap_uint _lut[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], xf::cv::Mat& dst) { int counterY = Tile.mTileHeight >> 1; int histY = -1; int counterX; int address = 0; RI: for (int i = 0; i < Tile.mRows; i++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=HEIGHT // clang-format on int counterX = (Tile.mTileWidth >> 1); int histX = -1; CI: for (int j = 0; j < Tile.mColsNPCAlligned; j++) { // clang-format off #pragma HLS LOOP_TRIPCOUNT min=1 max=COLS_NPC_ALIGNED #pragma HLS PIPELINE // clang-format on XF_TNAME(IN_TYPE, NPC) pxl; pxl = in.read(address); XF_TNAME(IN_TYPE, NPC) pxlout; NI: for (int k = 0; k < XF_NPIXPERCYCLE(NPC); k++) { // clang-format off #pragma HLS UNROLL // clang-format on ap_uint a; a = pxl.range(((k + 1) * XF_DTPIXELDEPTH(IN_TYPE, NPC)) - 1, (k * XF_DTPIXELDEPTH(IN_TYPE, NPC))); int Y1 = std::max(histY, 0); int X1 = std::max(histX, 0); int Y2 = histY + 1; if (Y2 >= Tile.mTilesY) Y2 = (Tile.mTilesY - 1); int X2 = (histX + 1); if (X2 >= Tile.mTilesX) X2 = (Tile.mTilesX - 1); ap_uint a1 = _lut[Y1][X1][2 * k][a]; ap_uint a2 = _lut[Y1][X2][2 * k][a]; ap_uint b1 = _lut[Y2][X1][2 * k + 1][a]; ap_uint b2 = _lut[Y2][X2][2 * k + 1][a]; int tileSize = Tile.mTileHeight * Tile.mTileWidth; int xa1 = Tile.mTileWidth - counterX; int xa = counterX; int ya1 = Tile.mTileHeight - counterY; int ya = counterY; ap_uint::cvalue> t1 = a1 * xa1 + a2 * xa; ap_uint::cvalue> t2 = b1 * xa1 + b2 * xa; ap_uint::cvalue + xf::cv::log2::cvalue> t3 = t1 * ya1 + t2 * ya; ap_uint::cvalue + xf::cv::log2::cvalue> t4 = t3 / tileSize; ap_uint d; if (t4 >= SATURATION_CAST) d = (1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)) - 1; else d = t4; pxlout.range(((k + 1) * XF_DTPIXELDEPTH(IN_TYPE, NPC)) - 1, (k * XF_DTPIXELDEPTH(IN_TYPE, NPC))) = d; } dst.write(address, pxlout); address++; counterX += (1 << XF_BITSHIFT(NPC)); if (counterX >= Tile.mTileWidthNPCAlligned) { counterX = 0; histX++; } } counterY++; if (counterY == (Tile.mTileHeight)) { histY++; counterY = 0; } } } void process_i(xf::cv::Mat& dst, xf::cv::Mat& in, _CLAHE_TILE& Tile, ap_uint _lutw[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _lutr[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _clipCounter[TILES_Y_MAX][TILES_X_MAX]) { xf::cv::Mat in_copy(in.rows, in.cols); // clang-format off #pragma HLS DATAFLOW // clang-format on populateLut(in, Tile, in_copy, _lutw, _clipCounter); interpolate(in_copy, Tile, _lutr, dst); } void process(xf::cv::Mat& dst, xf::cv::Mat& in, ap_uint _lutw[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _lutr[TILES_Y_MAX][TILES_X_MAX][(XF_NPIXPERCYCLE(NPC) << 1)] [1 << XF_DTPIXELDEPTH(IN_TYPE, NPC)], ap_uint _clipCounter[TILES_Y_MAX][TILES_X_MAX], int height, int width, int clip, int tilesY, int tilesX) { _CLAHE_TILE Tile(height, width, clip, tilesY, tilesX); process_i(dst, in, Tile, _lutw, _lutr, _clipCounter); } }; } // namespace clahe } // namespace cv } // namespace xf #endif