I am not very experinced when it comes to working with C++ and I was given some code from Andrey Smorodov when I use the method it is not manipulating the image. I believe it is being passed by value and once the method is done running the variables are gone. Can someone please tell me if passing by references vs value is what is wrong? When I comment out the method it does not change the result image.
Method:
void CalcBlockMeanVariance(cv::Mat Img,cv::Mat Res,float blockSide=21) // blockSide - the parameter (set greater for larger font on image)
{
cv::Mat I;
Img.convertTo(I,CV_32FC1);
Res=cv::Mat::zeros(Img.rows/blockSide,Img.cols/blockSide,CV_32FC1);
cv::Mat inpaintmask;
cv::Mat patch;
cv::Mat smallImg;
cv::Scalar m,s;
for(int i = 0;i < Img.rows-blockSide;i+=blockSide)
{
for (int j=0;j<Img.cols-blockSide;j+=blockSide)
{
patch=I(cv::Rect(j,i,blockSide,blockSide));
cv::meanStdDev(patch,m,s);
if(s[0]>0.01) // Thresholding parameter (set smaller for lower contrast image)
{
Res.at<float>(i/blockSide,j/blockSide)=m[0];
}else
{
Res.at<float>(i/blockSide,j/blockSide)=0;
}
}
}
cv::resize(I,smallImg,Res.size());
cv::threshold(Res,inpaintmask,0.02,1.0,cv::THRESH_BINARY);
cv::Mat inpainted;
smallImg.convertTo(smallImg,CV_8UC1,255);
inpaintmask.convertTo(inpaintmask,CV_8UC1);
inpaint(smallImg, inpaintmask, inpainted, 5, cv::INPAINT_TELEA);
cv::resize(inpainted,Res,Img.size());
Res.convertTo(Res,CV_32FC1,1.0/255.0);
}
Calling the method:
cv::Mat cvImage = [self cvMatFromUIImage:image];
cv::Mat res;
cv::cvtColor(cvImage, cvImage, CV_RGB2GRAY);
cvImage.convertTo(cvImage,CV_32FC1,1.0/255.0);
//CalcBlockMeanVariance(cvImage,res);
res=1.0-res;
res=cvImage+res;
cv::threshold(res,res, 0.85, 1, cv::THRESH_BINARY);
cv::resize(res, res, cv::Size(res.cols/2,res.rows/2));
cvImage.convertTo(cvImage,CV_8UC3,255.0);
_endImage = [self UIImageFromCVMat:cvImage];
Could I be losing any data when converting back to a UIImage?
Here is my resulting image:
The result Andrey got using this method:
Could anyone explain why I might be getting such a different result compared to Andrey?
Thanks
Change the function definition to the following:
void CalcBlockMeanVariance(const cv::Mat &Img, cv::Mat &Res, float blockSide=21)
Related
I am using OpenCV on iOS.
When I run the grabCut function below in a .mm file and supply:
a green "I" for inclStrokesImg
a red "E" for exclStrokesImg,
I expect to see a white "I" and "E" for inclStrokesDebug and exclStrokesDebug respectively, but now I am seeing
an "I" for `inclStrokesDebug" (expected)
but an "IE" for exclStrokesDebug (incorrect, expect to see E only)
cv::Mat cvt2Mask (UIImage * img) {
cv::Mat mask ;
if (img == nil){
return mask;
}
UIImageToMat(img, mask);
cv::cvtColor(mask, mask, CV_RGBA2GRAY);
cv::threshold(mask, mask, 1, 255, cv::THRESH_BINARY);
return mask;
}
+(UIImage *)grabCut:(UIImage *)srcImg withMask:(UIImage *)maskImg andInclusiveStrokes:(UIImage *)inclStrokesImg andExclusiveStrokes:(UIImage *)exclStrokesImg {
// Returns an inclusive mask image
cv::Mat src;
UIImageToMat(srcImg, src);
cv::Mat mask = cvt2Mask(maskImg);
cv::Mat inclStrokes = cvt2Mask(inclStrokesImg);
cv::Mat exclStrokes = cvt2Mask(exclStrokesImg);
#ifdef DEBUG
UIImage * maskDebug = MatToUIImage(mask);
UIImage * inclStrokesDebug = MatToUIImage(inclStrokes);
UIImage * exclStrokesDebug = MatToUIImage(exclStrokes);
#endif
...
return ...;
}
I checked the cvt2mask function, the UIImageToMat() went wrong and returned a result combined with previous results.
I ran the grabCut function again with the same parameters, the inclStrokesDebug will now return "IE" instead of "I" that I saw in the first call.
Is it due to some memory not released issues?
Problem seems fixed when I supply the alpha bool, for unknown reasons. I am using OpenCV 3.4.4
cv::Mat cvt2Mask (const UIImage * img) {
...
UIImageToMat(img, maskX, true);
...
return maskX;
}
I was trying to find a way how to make default parameter of Mat type, but it was complicated to make. But, today I found out this code OutputArray _hist = Mat() and I thought this could be simply a default parameter of Mat type. Therefore, I could make this code and it worked well, but I couldn't still understand one thing.
int myGetHistogram(InputArray _src, OutputArray _hist = Mat())
{
Mat src = _src.getMat();
_hist.create(512,512,CV_8U);
Mat histImage = _hist.getMat();
...
rectangle(histImage, max_pt1, max_pt2, Scalar(0), -1);
return max_pt1.x/(histImage.cols/256);
}
In this code, this error message shows up.
OpenCV Error: Assertion failed (!fixedSize() || ((Mat)obj)->size.operator()() == _sz)*
If I set the default parameter as OutputArray _hist = Mat(512,512,CV_8U) instead of OutputArray _hist = Mat(), then the error is removed.
Why does this problem happen?
In either case you're creating an OutputArray from a temporary Mat (i.e. this constructor), so you won't be able to change the size or datatype.
Take inspiration from the OpenCV code. Use cv::noArray() to make the output parameter optional, and then cv::OutputArray::needed to determine how to initialize your cv::Mat histImage.
#include <opencv2/opencv.hpp>
int myGetHistogram(cv::InputArray _src, cv::OutputArray _hist = cv::noArray())
{
cv::Mat src = _src.getMat();
cv::Size const HISTOGRAM_SIZE(512, 512);
cv::Mat histImage;
if (_hist.needed()) {
_hist.create(HISTOGRAM_SIZE, CV_8U);
histImage = _hist.getMat();
} else {
histImage = cv::Mat(HISTOGRAM_SIZE, CV_8UC1);
}
// ... whatever
return 1;
}
int main()
{
cv::Mat a(4, 4, CV_8UC1);
cv::Mat b;
myGetHistogram(a);
myGetHistogram(a, b);
return 0;
}
I'm trying to count object from image. I use logs photo, and I use some steps to get a binary image.
This is my code:
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <features2d.hpp>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
//load image
Mat img = imread("kayu.jpg", CV_LOAD_IMAGE_COLOR);
if(img.empty())
return -1;
//namedWindow( "kayu", CV_WINDOW_AUTOSIZE );
imshow("kayu", img);
//convert to b/w
Mat bw;
cvtColor(img, bw, CV_BGR2GRAY);
imshow("bw1", bw);
threshold(bw, bw, 40, 255, CV_THRESH_BINARY);
imshow("bw", bw);
//distance transform & normalisasi
Mat dist;
distanceTransform(bw, dist, CV_DIST_L2, 3);
normalize(dist, dist, 0, 2., NORM_MINMAX);
imshow("dist", dist);
//threshold to draw line
threshold(dist, dist, .5, 1., CV_THRESH_BINARY);
imshow("dist2", dist);
//dist = bw;
//dilasi
Mat dilation, erotion, element;
int dilation_type = MORPH_ELLIPSE;
int dilation_size = 17;
element = getStructuringElement(dilation_type, Size(2*dilation_size + 1, 2*dilation_size+1), Point(dilation_size, dilation_size ));
erode(dist, erotion, element);
int erotionCount = 0;
for(int i=0; i<erotionCount; i++){
erode(erotion, erotion, element);
}
imshow("erotion", erotion);
dilate(erotion, dilation, element);
imshow("dilation", dilation);
waitKey(0);
return 0;
}
As you can see, I use Erosion and Dilation to get better circular object of log. My problem is, I'm stuck at counting the object. I tried SimpleBlobDetector but I got nothing, because when I try to convert the result of "dilation" step to CV_8U, the white object disappear. I got error too when I use findContours(). It say something about channel of image. I can't show the error here, because that's too many step and I already delete it from my code.
Btw, at the end, i got 1 channel of image.
Can i just use it to counting, or am i have to convert it and what is the best method to do it?
Two simple steps:
Find contours for the binarized image.
Get the count of the contours.
Code:
int count_trees(const cv::Mat& bin_image){
cv::Mat img;
if(bin_image.channels()>1){
cv::cvtColor(bin_image,img,cv::COLOR_BGR2GRAY);
}
else{
img=bin_image.clone();;
}
if(img.type()!=CV_8UC1){
img*=255.f; //This could be stupid, but I do not have an environment to try it
img.convertTo(img,CV_8UC1);
}
std::vector<std::vector<cv::Point>> contours
std::vector<Vec4i> hierarchy;
cv::findContours( img, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
return contours.size();
}
I have the same problem, here's an idea I'm about to implement.
1) Represent your image as an array of integers; 0 = black, 1 = white.
2) set N = 2;
3) Scan your image, pixel-by-pixel. Whenever you find a white pixel, activate a flood-fill algorithm, starting at the pixel just found; paint the region with the value of N++;
4) Iterate 3 until you reach the last pixel. (N-2) is the number of regions found.
This method depends on the shape of the objects; mine are more chaotic than yours (wish me luck..). I'll make use of a recursive flood-fill recipe found somewhere (maybe Rosetta Code).
This solution also makes it easy to compute the size of each region.
try to apply that on the your deleted img
// count
for (int i = 0; i< contours.size(); i = hierarchy[i][0]) // iteration sur chaque contour .
{
Rect r = boundingRect(contours[i]);
if (hierarchy[i][2]<0) {
rectangle(canny_output, Point(r.x, r.y), Point(r.x + r.width, r.y + r.height), Scalar(20, 50, 255), 3, 8, 0);
count++;
}
}
cout << "Numeber of contour = " << count << endl;
imshow("src", src);
imshow("contour", dst);
waitKey(0);
I have a question which i am unable to resolve. I am taking difference of two images using OpenCV. I am getting output in a seperate Mat. Difference method used is the AbsDiff method. Here is the code.
char imgName[15];
Mat img1 = imread(image_path1, COLOR_BGR2GRAY);
Mat img2 = imread(image_path2, COLOR_BGR2GRAY);
/*cvtColor(img1, img1, CV_BGR2GRAY);
cvtColor(img2, img2, CV_BGR2GRAY);*/
cv::Mat diffImage;
cv::absdiff(img2, img1, diffImage);
cv::Mat foregroundMask = cv::Mat::zeros(diffImage.rows, diffImage.cols, CV_8UC3);
float threshold = 30.0f;
float dist;
for(int j=0; j<diffImage.rows; ++j)
{
for(int i=0; i<diffImage.cols; ++i)
{
cv::Vec3b pix = diffImage.at<cv::Vec3b>(j,i);
dist = (pix[0]*pix[0] + pix[1]*pix[1] + pix[2]*pix[2]);
dist = sqrt(dist);
if(dist>threshold)
{
foregroundMask.at<unsigned char>(j,i) = 255;
}
}
}
sprintf(imgName,"D:/outputer/d.jpg");
imwrite(imgName, diffImage);
I want to bound the difference part in a rectangle. findContours is drawing too many contours. but i only need a particular portion. My diff image is
I want to draw a single rectangle around all the five dials.
Please point me to right direction.
Regards,
I would search for the highest value for i index giving a non black pixel; that's the right border.
The lowest non black i is the left border. Similar for j.
You can:
binarize the image with a threshold. Background will be 0.
Use findNonZero to retrieve all points that are not 0, i.e. all foreground points.
use boundingRect on the retrieved points.
Result:
Code:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
// Load image (grayscale)
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// Binarize image
Mat1b bin = img > 70;
// Find non-black points
vector<Point> points;
findNonZero(bin, points);
// Get bounding rect
Rect box = boundingRect(points);
// Draw (in color)
Mat3b out;
cvtColor(img, out, COLOR_GRAY2BGR);
rectangle(out, box, Scalar(0,255,0), 3);
// Show
imshow("Result", out);
waitKey();
return 0;
}
Find contours, it will output a set of contours as std::vector<std::vector<cv::Point> let us call it contours:
std::vector<cv::Point> all_points;
size_t points_count{0};
for(const auto& contour:contours){
points_count+=contour.size();
all_points.reserve(all_points);
std::copy(contour.begin(), contour.end(),
std::back_inserter(all_points));
}
auto bounding_rectnagle=cv::boundingRect(all_points);
I'm trying to split two images along a seam, and then blend them together. In this process, I need to cut out each image along the seam by applying a mask. How can I apply a mask? I tried bitwise_and and multiplying the mask and the image, but neither worked.
int pano_width = left_template_width + right_template_width - roi_width;
// add zeros to the right of the left template
Mat full_left = Mat::zeros(roi_height, pano_width, CV_32FC3);
Mat tmp_l = full_left(Rect(0,0, left_template_width, roi_height));
imshow("Scene mask", mask0f3);
imshow("Cropped scene", cropped_scene);
Mat left_masked;
//bitwise_and(cropped_scene, mask0f3, left_masked); // full_left looks all black
multiply(cropped_scene, mask0f3, left_masked); // full_left looks like the scene mask, but with an extra black rectangle on the right side
left_masked.copyTo(tmp_l);
imshow("Full left", full_left);
I resorted to a terribly efficient, but working, hack:
void apply_mask(Mat& img, Mat mask) {
CV_Assert(img.rows == mask.rows);
CV_Assert(img.cols == mask.cols);
print_mat_type(img);
print_mat_type(mask);
for (int r = 0; r < mask.rows; r++) {
for (int c = 0; c < mask.cols; c++) {
if (mask.at<uchar>(r, c) == 0) {
img.at<Vec3f>(r, c) = Vec3f(0, 0, 0);
}
}
}
}
Here you have snippet that works using bitwise_and (look at docs how this methods works)
Mat img = imread("lena.jpg");
Mat mask = Mat::zeros(img.rows, img.cols, CV_8UC1);
Mat halfMask = mask(cv::Rect(0,0,img.rows/2, img.cols/2));
halfMask.setTo(cv::Scalar(255));
Mat left_masked;
bitwise_and(img, cv::Scalar(255,255,255), left_masked, mask);
So you can use something like:
bitwise_and(cropped_scene, cv::Scalar(255,255,255), left_masked, mask); // mask must be CV_8UC1!
But you have to change type, or create new mask, which has a type of CV_8UC1.
EDIT: Your function apply_mask can look like:
void apply_mask(Mat& img, Mat &mask, Mat &result) {
CV_Assert(img.rows == mask.rows);
CV_Assert(img.cols == mask.cols);
CV_Assert(img.type() == CV_32FC3);
bitwise_and(img, cv::Scalar(1.0f,1.0f,1.0f), result, mask);
}
Unfortunately if you pass input image as an output image in bitwise_and, you've got all black output. But passing another argument works fine.