I have the following simple program
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>\n");
return -1;
}
Mat1b image;
image = imread( argv[1], IMREAD_GRAYSCALE );
if ( !image.data )
{
printf("No image data \n");
return -1;
}
int rows= image.rows;
int cols= image.cols;
Mat1b taped_image(rows + 32, cols + 32);
Mat1b pyr_image = taped_image(Rect{16, 16, cols, rows});//<----HERE!!
// int t=image.type();
// Mat taped_image(rows + 32, cols + 32,t);
// Mat pyr_image = taped_image(Rect{16, 16, cols, rows});
cv::copyMakeBorder(image, taped_image, 16, 16, 16, 16, BORDER_REFLECT_101 + BORDER_ISOLATED);
// namedWindow("Display Image", WINDOW_AUTOSIZE );
// imshow("Display Image", image);
imshow("Tapped image",taped_image);
imshow("Display Image", pyr_image);
waitKey(0);
return 0;
}
When I run it, I got the taped image in a window "Tapped image" (as expected) and a black and white noise image in "Display Image. (When I used Mat instead of Mat1b the image was all black)
However I have the exact same thing in a much complex program and in that program apparently pyr_image got the original image.(as if it were a pointer)
Also in the documentation Mat definition 16 it says
Array that (as a whole or partly) is assigned to the constructed
matrix. No data is copied by these constructors. Instead, the header
pointing to m data or its sub-array is constructed and associated with
it. The reference counter, if any, is incremented. So, when you modify
the matrix formed using such a constructor, you also modify the
corresponding elements of m . If you want to have an independent copy
of the sub-array, use Mat::clone() .
So it seems that taped_image and pyr_image are associated. My questions are
Why it appears as noise to me?
Are pyr_image and image associated? (therefore having the same data)?
Related
I am able to convert an OpenCV mat object to an Eigen object, and back. However when I try to display the Eigen->Mat on the screen I get a blank window, and I do not know why. I can save the image to a file so I know its converting correctly.
Any suggestions on how to get the converted image to display on the screen?
Here is my current code:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include <opencv2/core/eigen.hpp>
int main(int argc, char **argv) {
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>\n");
return -1;
}
cv::Mat image;
image = cv::imread( argv[1], cv::ImreadModes::IMREAD_GRAYSCALE);
if ( !image.data )
{
printf("No image data \n");
return -1;
}
cv::namedWindow("Display Image", cv::WINDOW_AUTOSIZE );
cv::imshow("Display Image", image);
cv::waitKey(0);
Eigen::MatrixXd eigen_matrix;
cv::cv2eigen(image, eigen_matrix);
// std::cout << eigen_matrix << std::endl;
cv::Mat test_image;
cv::eigen2cv(eigen_matrix, test_image);
// This is blank
cv::namedWindow("Display Image2", cv::WINDOW_AUTOSIZE );
cv::imshow("Display Image2", test_image);
cv::waitKey(0);
cv::imwrite("test.png", test_image);
return 0;
}
From Eigen document, we can find this:
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
That's to say, you convert the grayscale image into double. While OpenCV display float/double in range [0, 1.0], save float/double in range [0, 255.0].
Two methods to solve:
imshow CV_32F|CV_64F multiplied by (1.0/255)
cv::imshow("doube image ", test_image*(1.0/255));
change Eigen Matrix element type to unsigned char
typedef Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic> MatrixXuc;
MatrixXuc eigen_matrix;
This is my result:
The code:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include <opencv2/core/eigen.hpp>
int main(int argc, char **argv) {
cv::Mat image = cv::imread( "Knight.jpg", cv::ImreadModes::IMREAD_GRAYSCALE);
if ( !image.data )
{
printf("No image data \n");
return -1;
}
cv::imshow("Source", image);
// (1) display multiplied by (1.0/255)
{
Eigen::MatrixXd eigen_matrix;
cv::cv2eigen(image, eigen_matrix);
cv::Mat test_image;
cv::eigen2cv(eigen_matrix, test_image);
cv::imshow("doube image ", test_image*(1.0/255));
cv::imwrite("dst_double.png", test_image);
}
// (2) change Eigen Matrix type
{
typedef Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic> MatrixXuc;
MatrixXuc eigen_matrix;
cv::cv2eigen(image, eigen_matrix);
cv::Mat test_image;
cv::eigen2cv(eigen_matrix, test_image);
cv::imshow("uchar image", test_image);
cv::imwrite("dst_uchar.png", test_image);
}
cv::waitKey(0);
return 0;
}
Notice:
Help on cv2.imshow
imshow(...)
imshow(winname, mat) -> None
. #brief Displays an image in the specified window.
.
. The function imshow displays an image in the specified window. If the window was created with the
. cv::WINDOW_AUTOSIZE flag, the image is shown with its original size, however it is still limited by $
. Otherwise, the image is scaled to fit the window. The function may scale the image, depending on its$
.
. - If the image is 8-bit unsigned, it is displayed as is.
. - If the image is 16-bit unsigned or 32-bit integer, the pixels are divided by 256. That is, the
. value range [0,255\*256] is mapped to [0,255].
. - If the image is 32-bit or 64-bit floating-point, the pixel values are multiplied by 255. That is$
. value range [0,1] is mapped to [0,255].
Help on cv2.imwrite
imwrite(...)
imwrite(filename, img[, params]) -> retval
. #brief Saves an image to a specified file.
.
. The function imwrite saves the image to the specified file. The image format is chosen based on the
. filename extension (see cv::imread for the list of extensions). Only 8-bit (or 16-bit unsigned (CV_1$
. in case of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with 'BGR' channel order) images
. can be saved using this function. If the format, depth or channel order is different, use
. Mat::convertTo , and cv::cvtColor to convert it before saving. Or, use the universal FileStorage I/O
. functions to save the image to XML or YAML format.
The previous answer only works on GrayScale, this one works on Color.
The key is to reshape the cv::Mat.
cv::Mat::reshape(int new_channel, int new_rows);
The result:
The code:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include <opencv2/core/eigen.hpp>
int main(int argc, char **argv) {
cv::Mat image = cv::imread( "Knight.jpg");
if ( !image.data )
{
printf("No image data \n");
return -1;
}
cv::imshow("Source", image);
int chs = image.channels();
// (1) display multiplied by (1.0/255)
{
cv::Mat img = image.reshape(1, 0);
std::cout << img.size() << ", " << img.channels() << std::endl;
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> MatrixXd;
MatrixXd mat;
cv::cv2eigen(img, mat);
cv::Mat img2;
cv::eigen2cv(mat, img2);
img2 = img2.reshape(chs, 0);
cv::imshow("doube color image ", img2*(1.0/255));
cv::imwrite("dst_double.png", img2);
}
// (2) change Eigen Matrix type
{
cv::Mat img = image.reshape(1, 0);
std::cout << img.size() << ", " << img.channels() << std::endl;
typedef Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic> MatrixXuc;
MatrixXuc mat;
cv::cv2eigen(img, mat);
cv::Mat img2;
cv::eigen2cv(mat, img2);
img2 = img2.reshape(chs, 0);
std::cout << img2.size() << ", " << img2.channels() << std::endl;
cv::imshow("uchar color image", img2);
cv::imwrite("dst_uchar.png", img2);
}
cv::waitKey(0);
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);
This question already has an answer here:
Why "cout" works weird for "unsigned char"?
(1 answer)
Closed 7 years ago.
Could help somebody please ?
I have an image after Canny detector, the type is 8UC1, when i want to access to the values, cout gives to me ? (Test Canny�), so my code is following:
Mat src;
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1; }
Mat src_gray(src.size[1],src.size[2],CV_8U,0);
//some parameters
int edgeThresh = 1;
//int lowThreshold;
int lowThreshold = 100;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
//char* window_name = "Edge Map";
cvtColor( src, src_gray, CV_BGR2GRAY );
Mat detected_edges;
/// Reduce noise with a kernel 3x3
blur( src_gray, detected_edges, Size(3,3) );
///Canny edge detection
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
for (unsigned int i=0;i<detected_edges.size[1];i++){
for (unsigned int j=0;j<detected_edges.size[1];j++) {
if (detected_edges.at<unsigned char>(i,j)!=0)
cout<<"Test Canny"<<detected_edges.at<unsigned char>(i,j)<<endl;
}
}
When I change in short, i.e. (i,j), it gives to me value between -256 and 255.
I do not understand why with the type 8UC1, i need to use short and is it correct to use short ?
(To verify surely which type I have, I used this link How to find out what type of a Mat object is with Mat::type() in OpenCV)
Thanks.
Suddenly, I found the answer, because uchar has some problems with cout, so i need to write
std::cout<<"Test Canny"<<(int)detected_edges.at(i,j)<
I have a problem with this code:
The problem is when I see the image original, is modified by "borrarFondo()" but this function is called from "segmentarHoja" and here entry img by value, but img modifies.
void borrarFondo(Mat& img){
img = ~img;
Mat background;
medianBlur(img, background, 45);
GaussianBlur(background, background, Size(203,203),101,101);
img = img - background;
img = ~img;
}
void segmentarHoja(Mat img, Mat& imsheet){
Mat imgbw;
borrarFondo(img); //borrarFondo is called from here where img is a copy
cvtColor(img, imgbw, CV_BGR2GRAY);
threshold(imgbw, imgbw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(21,21));
erode(imgbw, imgbw, element);
vector<vector<Point> > contoursSheet;
findContours(imgbw, contoursSheet, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
vector<Rect> boundSheet(contoursSheet.size());
int largest_area=0;
for( int i = 0; i< contoursSheet.size(); i++ )
{
double a= contourArea( contoursSheet[i],false);
if(a>largest_area){
largest_area=a;
boundSheet[i] = boundingRect(contoursSheet[i]);
imsheet=img(boundSheet[i]).clone();
}
}
borrarFondo(imsheet);
}
int main()
{
Mat imsheet;
image= imread("c:/imagen.jpg");
segmentarHoja(image, imsheet);
imshow("imsheet",imsheet);
imshow("imagen",image); //original image by amending borrarFondo
waitKey(0);
}
I don't want to change original image
opencv Mat is a counted reference (i.e. like std::shared_ptr, except different syntax) where copy construction or assignment does not copy. use the clone method to copy. read the documentation, always a good idea.
if you're doing something like this:
Mat a;
Mat b = a;
or like this:
void func(Mat m) {...}
or :
vector<Mat> vm;
vm.push_back(m);
all of it is a shallow copy. the Mat header will be a copy, the pointers inside, too.
so, e.g. in the 1st example, b and a share the same size and data members
this might explain, why passing a Mat by value still results in pixels manipulated from the 'shallow' copy.
to avoid that you will have to do a 'deep' copy instead:
Mat c = a.clone(); // c has its own pixels now.
and again, if you don't want your Mat to be manipulated, pass it as a const Mat & be very careful about how you use it, as illustrated below.
#include <opencv2/opencv.hpp>
void foo( cv::Mat const& image )
{
cv::Mat result = image;
cv::ellipse(
result, // img
cv::Point( 300, 300 ), // center
cv::Size( 50, 50 ), // axes (bounding box size)
0.0, // angle
0.0, // startAngle
360.0, // endAngle
cv::Scalar_<int>( 0, 0, 255 ), // color
6 // thickness
);
}
auto main() -> int
{
auto window_name = "Display";
cv::Mat lenna = cv::imread( "lenna.png" );
foo( lenna );
imshow( window_name, lenna );
cv::waitKey( 0 );
}
The Mat const& lied about mutability, and Lenna’s nose is correspondingly long, here marked by a big fat circle placed by the foo function above:
I am just starting to use the Open CV library and one of my first code is a simple negative transform function.
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void negative(Mat& input,Mat& output)
{
int row = input.rows;
int col = input.cols;
int x,y;
uchar *input_data=input.data;
uchar *output_data= output.data;
for( x=0;x<row;x++)
for( y=0;y<col;y++)
output_data[x*col+y]=255-input_data[x*col+y];
cout<<x<<y;
}
int main( int argc, char** argv )
{
Mat image;
image = imread( argv[1], 1 );
Mat output=image.clone();
negative(image,output);
namedWindow( "Display Image", CV_WINDOW_AUTOSIZE );
imshow( "Display Image", output );
waitKey(0);
return 0;
}
I have added the extra line to check if the entire image is processed. The problem i am facing with my output image is that negative transform is applied only to top half of the image.
Now what happens is that the values for x and y are displayed only after i press a key (i.e. once the image is shown)
My question is why is the window being called before the function is executed ?
The fundamental problem in your code is that you are reading in a color image but you try to process it as grayscale. Therefore the indices shift and what really happens is that you only process the first third of the image (because of the 3-channel format).
See opencv imread manual
flags –
Specifies color type of the loaded image:
>0 the loaded image is forced to be a 3-channel color image
=0 the loaded image is forced to be grayscale
You've specified flags=1.
Here's a way of doing it:
Vec3b v(255, 255, 255);
for(int i=0;i<input.rows;i++) //search for edges
{
for (int j=0 ;j<input.cols;j++)
{
output.at<Vec3b>(i,j) = v - input.at<Vec3b>(i,j);
}
}
Note that here Vec3b is a 3-channel pixel value as opposed to uchar which is a 1-channel value.
For a more efficient implementation you can have a look at Mat.ptr<Vec3b>(i).
EDIT:
If you are processing lots of images,
for a general iteration over the pixels the fastest way is:
Vec3b v(255, 255, 255); // or maybe Scalar v(255,255,255) Im not sure
for(int i=0;i<input.rows;i++) //search for edges
{
Vec3b *p=input.ptr<Vec3b>(i);
Vec3b *q=output.ptr<Vec3b>(i);
for (int j=0 ;j<input.cols;j++)
{
q[j] = v - p[j];
}
}
See "The OpenCV Tutorials" -- "The efficient way" section.
Try to write:
cout << x << y << endl;
The function is called before, but the output is not flushed directly, which results in your image appearing before the text is written. By adding an "endline", you force a flush. You could also use flush(cout); instead of adding and endline.
For the negative, you can use the OpenCV function subtract() directly:
subtract(Scalar(255, 255, 255), input, output);