This is an example of how to post-process after seg module. More details about this example.
#include <algorithm>
#include <numeric>
#define SR_FILTER_OBJECTS_BY_APPROXIMATE_SIZE
std::vector<cv::Scalar> colorsForSkeleton{
cv::Scalar(255, 0, 0),
cv::Scalar(0, 0, 255),
cv::Scalar(0, 255, 0),
cv::Scalar(255, 0, 255),
cv::Scalar(0, 255, 255),
cv::Scalar(255, 255, 0)
};
int main()
{
std::wstring data_dir = L"../../../examples/data";
std::wstring trainedModelPath = data_dir + L"/models/stackcell_sample.srSeg";
std::vector<std::wstring> imagePaths(1);
imagePaths[0] = data_dir + L"/images/StackCell/sample.png";
unsigned char* ucImageData = nullptr;
unsigned int* uiImageData = nullptr;
int verbosity = 3;
double overlayAlpha = 0.7;
int displayTime = 100;
double resizeFactor = 0.5;
int gpuIndex = 0;
if (handle == nullptr)
{
std::wcout << "Saige Vision failed: Please check the file path of trained model." << std::endl;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
int numberOfClassesOutput = 0;
int maxStrLenOfClassName = 0;
{
PrintErrorType(srStatus, verbosity);
return -1;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
std::vector<std::string> classNames(numberOfClassesOutput);
std::vector<std::wstring> wClassNames(numberOfClassesOutput);
std::vector<int> classColorsOutput(numberOfClassesOutput);
char* classNameOutput = new char[maxStrLenOfClassName];
wchar_t* wClassNameOutput = new wchar_t[maxStrLenOfClassName];
for (int i = 0; i < numberOfClassesOutput; i++)
{
{
PrintErrorType(srStatus, verbosity);
return -1;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
classNames[i] = classNameOutput;
wClassNames[i] = wClassNameOutput;
}
delete[] classNameOutput;
delete[] wClassNameOutput;
std::vector<int> scoreThresholdOutput(numberOfClassesOutput);
std::vector<int> pixelSizeThresholdOutput(numberOfClassesOutput);
{
PrintErrorType(srStatus, verbosity);
return -1;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
# ifdef SR_USE_NEW_SCORE_THRESHOLD
std::vector<int> scoreThreshold(numberOfClassesOutput);
for (int i = 0; i < numberOfClassesOutput; i++)
{
scoreThreshold[i] = 100;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
# ifdef SR_USE_NEW_PIXEL_SIZE_THRESHOLD
std::vector<int> pixelSizeThreshold(numberOfClassesOutput);
for (int i = 0; i < numberOfClassesOutput; i++)
{
pixelSizeThreshold[i] = 200;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
pixelSizeThresholdOutput = pixelSizeThreshold;
#endif
#if (!defined(SR_FILTER_OBJECTS_BY_EXACT_SIZE)) && (!defined(SR_FILTER_OBJECTS_BY_APPROXIMATE_SIZE))
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifdef SR_FILTER_OBJECTS_BY_EXACT_SIZE
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifdef SR_FILTER_OBJECTS_BY_APPROXIMATE_SIZE
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifdef SR_CROP_AND_STORE_SEGMENTED_OBJECT
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifdef SR_GET_SCORES_OF_SEGMENTED_OBJECT
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifdef SR_GET_AVERAGE_COLOR_OF_SEGMENTED_OBJECT
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifdef SR_USE_FORWARD_TIME_LIMIT
float timeLimit = 1000;
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
#ifndef SR_USE_FORWARD_TIME_LIMIT
{
PrintErrorType(srStatus, verbosity);
return -1;
}
#endif
SR_SegPostProcessOptions segPostProcessOptions;
segPostProcessOptions.nProducts = 3;
segPostProcessOptions.nTargetTopsPerProduct = 26;
segPostProcessOptions.nTargetBottomsPerProduct = 25;
segPostProcessOptions.topClassIdx = 0;
segPostProcessOptions.bottomClassIdx = 1;
segPostProcessOptions.convexityDefectThreshold = 30;
segPostProcessOptions.useImageDataAtTop = true;
segPostProcessOptions.isTopBrighterThanBackGround = true;
segPostProcessOptions.useImageDataAtBottom = true;
segPostProcessOptions.isBottomBrighterThanBackGround = true;
segPostProcessOptions.maxLengthOfSkeletonToRemoveAtTop = 5.0f;
segPostProcessOptions.maxRatioThresholdOfMedianLengthToRemoveAtTop = 0.5f;
segPostProcessOptions.minLengthOfSkeletonToKeepAtTop = 50.0f;
segPostProcessOptions.maxLengthOfSkeletonToRemoveAtBottom = 10.0f;
segPostProcessOptions.maxRatioThresholdOfMedianLengthToRemoveAtBottom = 0.5f;
segPostProcessOptions.minLengthOfSkeletonToKeepAtBottom = 40.0f;
{
PrintErrorType(srStatus, verbosity);
return -1;
}
for (size_t i_img = 0; i_img < imagePaths.size(); i_img++)
{
int h = 0, w = 0;
switch (inputImageType)
{
case eSRInputImageType::UCGrayImage:
case eSRInputImageType::UCColorImage:
case eSRInputImageType::UCGrayImage16Bit:
case eSRInputImageType::UCColorImage16Bit:
break;
case eSRInputImageType::UIColorImage:
case eSRInputImageType::UIColorImage16Bit:
break;
}
if (inputImageType == eSRInputImageType::ImageFilePathMultiByteChar)
{
size_t sizeOfChar = 0;
size_t sizeOfWChar = wcslen(imagePaths[i_img].c_str());
size_t bufferSize = sizeOfWChar * 2;
std::vector<char> imagePathMB(bufferSize);
sizeOfChar = wcstombs(
imagePathMB.data(),
imagePaths[i_img].c_str(),
bufferSize
);
if (sizeOfChar != sizeOfWChar)
{
std::wcout << "Saige Vision FAILED: Wrong Image path" << imagePaths[i_img] << std::endl;
continue;
}
}
else if (inputImageType == eSRInputImageType::ImageFilePathWideChar)
{
}
else if (inputImageType == eSRInputImageType::UCGrayImage)
{
}
else if (inputImageType == eSRInputImageType::UCColorImage)
{
}
else if (inputImageType == eSRInputImageType::UIColorImage)
{
}
else if (inputImageType == eSRInputImageType::UCGrayImage16Bit)
{
}
else if (inputImageType == eSRInputImageType::UCColorImage16Bit)
{
}
else if (inputImageType == eSRInputImageType::UIColorImage16Bit)
{
}
{
PrintErrorType(srStatus, verbosity);
if (verbosity >= 3)
{
}
continue;
}
if (inputImageType == eSRInputImageType::ImageFilePathWideChar || inputImageType == eSRInputImageType::ImageFilePathMultiByteChar)
{
{
PrintErrorType(srStatus, verbosity);
continue;
}
}
long inspectionTimeOutput = 0, loadImageTimeOutput = 0;
{
PrintErrorType(srStatus, verbosity);
continue;
}
{
PrintErrorType(srStatus, verbosity);
continue;
}
int matrixChannelOutput = 0, matrixSizeOutput = 0;
std::vector<unsigned char> outputMatrix;
outputMode == SR_MULTI_CLASS_HEATMAP_OUTPUT)
{
{
PrintErrorType(srStatus, verbosity);
continue;
}
outputMatrix.resize(matrixSizeOutput);
{
PrintErrorType(srStatus, verbosity);
continue;
}
}
std::vector<SR_SegmentedObject> segmentedObject;
std::vector<SR_SegmentedObject> deletedObject;
{
int nSegmentedObjects = 0;
{
PrintErrorType(srStatus, verbosity);
continue;
}
segmentedObject.resize(nSegmentedObjects);
{
PrintErrorType(srStatus, verbosity);
continue;
}
if (verbosity >= 2)
{
std::cout << " - the number of segmented objects: " << segmentedObject.size() << std::endl;
for (int i = 0; i < segmentedObject.size(); i++)
{
std::cout << " - " << i << "-th segmented objects : " <<
"class[" << segmentedObject[i].classIdx << "], " <<
"area[" << segmentedObject[i].area << "], " <<
"width[" << segmentedObject[i].width << "], " <<
"height[" << segmentedObject[i].height << "], " <<
"x_center[" << segmentedObject[i].xCenter << "], " <<
"y_center[" << segmentedObject[i].yCenter << "], " <<
"scores(average, min, max)[" << segmentedObject[i].scores[0] << ", "
<< segmentedObject[i].scores[1] << ", "
<< segmentedObject[i].scores[2] << "], "
"average_color(B, G, R)[" << segmentedObject[i].averageColor[0] << ", " <<
segmentedObject[i].averageColor[1] << ", " <<
segmentedObject[i].averageColor[2] << "]" << std::endl;
}
}
int nDeletedObjects = 0;
{
PrintErrorType(srStatus, verbosity);
continue;
}
deletedObject.resize(nDeletedObjects);
{
PrintErrorType(srStatus, verbosity);
continue;
}
if (verbosity >= 2)
{
std::cout << " - the number of deleted objects: " << deletedObject.size() << std::endl;
}
}
auto startPostProcess = std::chrono::system_clock::now();
if (inputImageType == eSRInputImageType::ImageFilePathMultiByteChar)
{
size_t sizeOfChar = 0;
size_t sizeOfWChar = wcslen(imagePaths[i_img].c_str());
size_t bufferSize = sizeOfWChar * 2;
std::vector<char> imagePathMB(bufferSize);
sizeOfChar = wcstombs(
imagePathMB.data(),
imagePaths[i_img].c_str(),
bufferSize
);
if (sizeOfChar != sizeOfWChar)
{
std::wcout << "Saige Vision FAILED: Wrong Image path" << imagePaths[i_img] << std::endl;
continue;
}
}
else if (inputImageType == eSRInputImageType::ImageFilePathWideChar)
{
}
else if (inputImageType == eSRInputImageType::UCGrayImage)
{
}
else if (inputImageType == eSRInputImageType::UCColorImage)
{
}
else if (inputImageType == eSRInputImageType::UIColorImage)
{
}
else if (inputImageType == eSRInputImageType::UCGrayImage16Bit)
{
}
else if (inputImageType == eSRInputImageType::UCColorImage16Bit)
{
}
else if (inputImageType == eSRInputImageType::UIColorImage16Bit)
{
}
{
PrintErrorType(srStatus, verbosity);
continue;
}
{
PrintErrorType(srStatus, verbosity);
if (verbosity >= 3)
{
}
continue;
}
auto durationPostProcess = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startPostProcess);
int nSegSkeletonsOutput = 0;
{
PrintErrorType(srStatus, verbosity);
continue;
}
std::vector<SR_SegPostProcessSkeleton> segSkeletons(nSegSkeletonsOutput);
{
PrintErrorType(srStatus, verbosity);
continue;
}
std::vector<int> topCounts;
std::vector<int> bottomCounts;
for (int i = 0; i < segPostProcessOptions.nProducts; i++)
{
auto top = std::count_if(segSkeletons.begin(), segSkeletons.end(),
[i, segPostProcessOptions](SR_SegPostProcessSkeleton&a) {
return a.idxClass == segPostProcessOptions.topClassIdx && a.idxProduct == i; });
topCounts.push_back(top);
auto bottom = std::count_if(segSkeletons.begin(), segSkeletons.end(),
[i, segPostProcessOptions](SR_SegPostProcessSkeleton&a) {
return a.idxClass == segPostProcessOptions.bottomClassIdx && a.idxProduct == i; });
bottomCounts.push_back(bottom);
}
if (verbosity >= 2)
{
std::cout << "====== segmentation Test Result ======" << std::endl;
std::wcout << imagePaths[i_img] << std::endl;
std::cout << " - Image width, height: " << w << ", " << h << std::endl;
std::cout << " - Image Loading time: " << loadImageTimeOutput << std::endl;
std::cout << " - Inspection time: " << inspectionTimeOutput << std::endl;
std::cout << " - Post-processing time: " << static_cast<double>(durationPostProcess.count()) << std::endl;
std::cout << " - top skeletons: " << std::accumulate(topCounts.begin(), topCounts.end(), 0) << " ( ";
for (auto cnt : topCounts) {
std::cout << cnt << " ";
}
std::cout << ")" << std::endl;
std::cout << " - bottom skeletons: " << std::accumulate(bottomCounts.begin(), bottomCounts.end(), 0) << " ( ";
for (auto cnt : bottomCounts) {
std::cout << cnt << " ";
}
std::cout << ")" << std::endl;
std::cout << std::endl;
}
if (verbosity >= 3)
{
cv::Mat image = cv::Mat();
if (inputImageType == eSRInputImageType::UCGrayImage)
{
image = cv::Mat(h, w, CV_8UC1, ucImageData);
image = image.clone();
}
else if (inputImageType == eSRInputImageType::UCColorImage)
{
image = cv::Mat(h, w, CV_8UC3, ucImageData);
image = image.clone();
}
else if (inputImageType == eSRInputImageType::UIColorImage)
{
image = cv::Mat(h, w, CV_8UC4, uiImageData);
image = image.clone();
}
else if (inputImageType == eSRInputImageType::UCGrayImage16Bit)
{
image = cv::Mat(h, w, CV_16UC1, ucImageData);
image.convertTo(image, CV_8U, 1.0 / 256.0);
image = image.clone();
}
else if (inputImageType == eSRInputImageType::UCColorImage16Bit)
{
image = cv::Mat(h, w, CV_16UC3, ucImageData);
image.convertTo(image, CV_8U, 1.0 / 256.0);
image = image.clone();
}
else if (inputImageType == eSRInputImageType::UIColorImage16Bit)
{
image = cv::Mat(h, w, CV_16UC4, uiImageData);
image.convertTo(image, CV_8U, 1.0 / 256.0);
image = image.clone();
}
else
{
if (isSuccess == 0)
{
std::cout << "Saige Vision FAILED: Fail to open image file to display" << std::endl;
continue;
}
}
{
if (image.channels() == 4)
{
cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);
}
cv::Mat imageCheck = image.clone();
unsigned char* data = imageCheck.data;
{
if (image.channels() == 1)
{
for (int i_pixel = 0; i_pixel < matrixSizeOutput; i_pixel++)
{
if (outputMatrix[i_pixel] > 0)
{
data[i_pixel] = outputMatrix[i_pixel];
}
}
}
else if (image.channels() == 3)
{
for (int i_pixel = 0; i_pixel < matrixSizeOutput; i_pixel++)
{
if (outputMatrix[i_pixel] > 0)
{
data[3 * i_pixel + 0] = 0;
data[3 * i_pixel + 1] = 0;
data[3 * i_pixel + 2] = outputMatrix[i_pixel];
}
}
}
}
{
cv::Scalar whiteScalar = cv::Scalar(255.0);
if (image.channels() == 1)
{
whiteScalar = cv::Scalar(255.0);
}
else if (image.channels() == 3)
{
whiteScalar = cv::Scalar(0, 0, 255.0);
}
for (int i_c = 0; i_c < segmentedObject.size(); i_c++)
{
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::drawContours(imageCheck, contours, -1, whiteScalar, -1, 8, hierarchy);
}
}
cv::Mat finalUnifiedImage;
double beta = 1.0 - overlayAlpha;
double gamma = 0;
cv::addWeighted(image, overlayAlpha, imageCheck, beta, gamma, finalUnifiedImage);
cv::namedWindow("Segmentation Result", cv::WINDOW_NORMAL);
cv::resizeWindow("Segmentation Result", w * resizeFactor, h * resizeFactor);
cv::imshow("Segmentation Result", finalUnifiedImage);
cv::waitKey(displayTime);
}
{
if (image.channels() == 4)
{
cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);
}
else if (image.channels() == 1)
{
cv::cvtColor(image, image, cv::COLOR_GRAY2BGR);
}
cv::Mat imageCheck = image.clone();
{
unsigned char* data = imageCheck.data;
for (int i_pixel = 0; i_pixel < w * h; i_pixel++)
{
int maxVal = 0;
int channel = 0;
for (int c = 0; c < numberOfClassesOutput; c++)
{
if (outputMatrix[i_pixel + c * w * h] > maxVal)
{
maxVal = outputMatrix[i_pixel + c * w * h];
channel = c;
}
}
unsigned char* tmp = (unsigned char*)(&classColorsOutput[channel]);
int b = 0, g = 0, r = 0;
std::memcpy(&b, tmp, 1);
std::memcpy(&g, tmp + 1, 1);
std::memcpy(&r, tmp + 2, 1);
if (maxVal != 0)
{
data[3 * i_pixel + 0] = b * (maxVal / 255.0);
data[3 * i_pixel + 1] = g * (maxVal / 255.0);
data[3 * i_pixel + 2] = r * (maxVal / 255.0);
}
}
}
{
for (int i_c = 0; i_c < segmentedObject.size(); i_c++)
{
unsigned char* tmp = (unsigned char*)(&classColorsOutput[segmentedObject[i_c].classIdx]);
int b = 0, g = 0, r = 0;
std::memcpy(&b, tmp, 1);
std::memcpy(&g, tmp + 1, 1);
std::memcpy(&r, tmp + 2, 1);
cv::Scalar overlayScalar = cv::Scalar(b, g, r);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::drawContours(imageCheck, contours, -1, overlayScalar, -1, 8, hierarchy);
}
}
cv::Mat finalUnifiedImage;
double beta = 1.0 - overlayAlpha;
double gamma = 0.0;
cv::addWeighted(image, overlayAlpha, imageCheck, beta, gamma, finalUnifiedImage);
cv::namedWindow("Segmentation Result", cv::WINDOW_NORMAL);
cv::resizeWindow("Segmentation Result", w * resizeFactor, h * resizeFactor);
cv::imshow("Segmentation Result", finalUnifiedImage);
cv::waitKey(displayTime);
#ifdef SR_CROP_AND_STORE_SEGMENTED_OBJECT
#endif
}
{
int iterSkelColor = 0;
cv::Mat imageCheck = image.clone();
cv::Mat imageCheck2 = image.clone();
for (int i_s = 0; i_s < segSkeletons.size(); i_s++)
{
unsigned char* tmp = (unsigned char*)(&classColorsOutput[segSkeletons[i_s].idxClass]);
int b = 0, g = 0, r = 0;
std::memcpy(&b, tmp, 1);
std::memcpy(&g, tmp + 1, 1);
std::memcpy(&r, tmp + 2, 1);
cv::Scalar overlayScalar = cv::Scalar(b, g, r);
for (int i_p = 0; i_p < segSkeletons[i_s].nPoints; i_p++)
{
cv::Point pt0(segSkeletons[i_s].xPts[i_p], segSkeletons[i_s].yPts[i_p]);
cv::circle(imageCheck, pt0, 1, colorsForSkeleton[iterSkelColor % colorsForSkeleton.size()], 1);
}
for (int i_p = 0; i_p < segSkeletons[i_s].nPoints - 1; i_p++)
{
cv::Point pt0(segSkeletons[i_s].xPts[i_p], segSkeletons[i_s].yPts[i_p]);
cv::Point pt1(segSkeletons[i_s].xPts[i_p + 1], segSkeletons[i_s].yPts[i_p + 1]);
cv::line(imageCheck, pt0, pt1, overlayScalar, 1);
cv::line(imageCheck2, pt0, pt1, overlayScalar, 3);
}
iterSkelColor++;
}
cv::Mat finalUnifiedImage;
double beta = 1.0 - overlayAlpha;
double gamma = 0.0;
cv::addWeighted(image, overlayAlpha, imageCheck, beta, gamma, finalUnifiedImage);
cv::namedWindow("Post-process Result", cv::WINDOW_NORMAL);
cv::resizeWindow("Post-process Result", w * resizeFactor, h * resizeFactor);
cv::imshow("Post-process Result", finalUnifiedImage);
cv::waitKey(displayTime);
cv::Mat finalUnifiedImage2;
cv::addWeighted(image, overlayAlpha, imageCheck2, beta, gamma, finalUnifiedImage2);
cv::namedWindow("Post-process Result2", cv::WINDOW_NORMAL);
cv::resizeWindow("Post-process Result2", w * resizeFactor, h * resizeFactor);
cv::imshow("Post-process Result2", finalUnifiedImage2);
cv::waitKey(displayTime);
}
}
if (ucImageData != nullptr)
{
delete[] ucImageData;
}
if (uiImageData != nullptr)
{
delete[] uiImageData;
}
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
{
PrintErrorType(srStatus, verbosity);
return -1;
}
std::cout << "SaigeVision successfully completed, please press enter key to terminate" << std::endl;
std::cin.get();
return 0;
}