I want to implement a background averaging method. I have 50 frames of images taken in one second and some of the frames contain lightning which I want to extract as the foreground. The frames are taken with a stationary camera and the frames are taken as grayscales. What I want to do is:
Get the background model
After, compare each frame to the background model to determine whether there is lighting in that frame or not.
I read some documents on how this can possible be done by using cvAcc() but am having a difficulty understanding how this can be done. I would appreciate a piece of code which guide me and links to documents that can help me understand how I can implement this.
Thanking you in advance.
We had the same task in one of our projects.
To get the background model, we simply create a class BackgroundModel, capture the first (lets say) 50 frames and calculate the average frame to avoid pixel errors in the background model.
For example, if you get an 8-bit greyscale image (CV_8UC1) from your camera, you initialize your model with CV_16UC1 to avoid clipping.
cv::Mat model = cv::Mat(HEIGHT, WIDTH, CV_16UC1, cv::Scalar(0));
Now, waiting for the first frames to calculate your model, just add every frame to the model and count the amount of received frames.
void addFrame(cv::Mat frame) {
cv::Mat convertedFrame;
frame.convertTo(convertedFrame, CV_16UC1);
cv::add(convertedFrame, model, model);
if (++learnedFrames >= FRAMES_TO_LEAN) { // FRAMES_TO_LEARN = 50
createMask();
}
}
The createMask() function calculates the average frame which we use for the model.
void createMask() {
cv::convertScaleAbs(model, mask, 1.0 / learnedFrames);
mask.convertTo(mask, CV_8UC1);
}
Now, you just send all the frames the way through the BackgroundModel class to a function subtract(). If the result is an empty cv::Mat, the mask is still calculated. Otherwise, you get a subtracted frame.
cv::Mat subtract(cv::Mat frame) {
cv::Mat result;
if (++learnedFrames >= FRAMES_TO_LEAN) { // FRAMES_TO_LEARN = 50
cv::subtract(frame, mask, result);
}
else {
addFrame(frame);
}
return result;
}
Last but not least, you can use
Scalar sum(const Mat& mtx)
to calculate the pixel sum and decide if it's a frame with lights on it.
MyPolygon function mask the ROI and after that, it calculates the abs Pixel difference and calculates the number of white pixels.
srcImage : Reference image.
#include <opencv2/opencv.hpp>
#include <iostream>
#include <random>
using namespace std;
using namespace cv;
cv::Mat MyPolygon( Mat img )
{
int lineType = 8;
// [(892, 145), (965, 150), (933, 199), (935, 238), (970, 248), (1219, 715), (836, 709), (864, 204)]
/** Create some points */
Point rook_points[1][8];
rook_points[0][0] = Point(892, 145);
rook_points[0][1] = Point(965, 150);
rook_points[0][2] = Point(933, 199);
rook_points[0][3] = Point(935, 238);
rook_points[0][4] = Point(970, 248);
rook_points[0][5] = Point(1219, 715);
rook_points[0][6] = Point(836, 709);
rook_points[0][7] = Point(864, 204);
const Point* ppt[1] = { rook_points[0] };
int npt[] = { 8 };
cv::Mat mask = cv::Mat::zeros(img.size(), img.type());
fillPoly( mask,
ppt,
npt,
1,
Scalar( 255, 0, 0 ),
lineType
);
cv::bitwise_and(mask,img, img);
return img;
}
int main() {
/* code */
cv::Mat srcImage = cv::imread("/home/gourav/Pictures/L1 Image.png", cv::IMREAD_GRAYSCALE);
resize(srcImage, srcImage, Size(1280, 720));
// cout << " Width : " << srcImage.cols << endl;
// cout << " Height: " << srcImage.rows << endl;
if (srcImage.empty()){
std::cerr<<"Ref Image not found\n";
return 1;
}
cv::Mat img = MyPolygon(srcImage);
Mat grayBlur;
GaussianBlur(srcImage, grayBlur, Size(5, 5), 0);
VideoCapture cap("/home/gourav/GenralCode/LD3LF1_stream1.mp4");
Mat frames;
if(!cap.isOpened()){
std::cout << "Error opening video stream or file" << endl;
return -1;
}
while (1)
{
cap >> frames;
if (frames.empty())
break;
// Convert current frame to grayscale
cvtColor(frames, frames, COLOR_BGR2GRAY);
// cout << "Frame Width : " << frames.cols << endl;
// cout << "Frame Height: " << frames.rows << endl;
Mat imageBlure;
GaussianBlur(frames, imageBlure, Size(5, 5), 0);
cv::Mat frame = MyPolygon(imageBlure);
Mat dframe;
absdiff(frame, grayBlur, dframe);
// imshow("grayBlur", grayBlur);
// Threshold to binarize
threshold(dframe, dframe, 30, 255, THRESH_BINARY);
//White Pixels
int number = cv::countNonZero(dframe);
cout<<"Count: "<< number <<"\n";
if (number > 3000)
{
cout<<"generate Alert ";
}
// Display Image
imshow("dframe", dframe);
char c=(char)waitKey(25);
if (c==27)
break;
}
cap.release();
return 0;
}
Related
im beginner in Opencv with c++. I have to draw a filled rectangle(10x10) in the middle of a image where every 5th pixel is black.
i Know how to create a rectangle. But how i can fill it and change the color of every 5th pixel ?
Would be nice if someone can help :/
void cv::rectangle ( InputOutputArray img,
Point pt1,
Point pt2,
const Scalar & color,
int thickness = 1,
int lineType = LINE_8,
int shift = 0
)
My code so far:
#include "opencv2/opencv.hpp"
#include<sstream>
using namespace std;
using namespace cv;
int main(void)
{
//Laden vom Bild
Mat img;
img = imread("C:\\Users\\Mehmet\\Desktop\\yoshi.png");
if (!img.data)
{
cout << "Could not find the image";
return -1;
}
namedWindow("window");
imshow("window", img);
imwrite("C:\\Users\\Max Mustermann\\Desktop\\11.png", img);
cv::Size sz = img.size();
int imageWidth = sz.width;
int imageHeight = sz.height;
cout <<"Es gibt " <<img.channels()<<" Farbkanäle" << endl;;
cout << "Die Breite betreagt: "<<sz.width << endl;
cout <<"Die Hoehe betreagt: " << sz.height<<endl;
std::cout << img.type();
Mat img1;
img.convertTo(img1, CV_32FC3, 1 / 255.0);
waitKey(0);
return 0;
}
```
You may be able to find the answer to your question in the opencv document.
To fill the rectangle, you can change the parameter 'thickness'
==> 'thickness Thickness of lines that make up the rectangle. Negative values, like FILLED, mean that the function has to draw a filled rectangle.'
Link:
https://docs.opencv.org/4.5.2/d6/d6e/group__imgproc__draw.html#ga07d2f74cadcf8e305e810ce8eed13bc9
And, changing color can be done by the color parameter. Controlling this parameter is easy with using cv::Scalar(BLUE, GREEN, RED).
For example, Rectangle(~~~,cv::Scalar(255,0,0),~~~); will make blue colorized rectangle with depending other parameters. So, if you want to change the color, change these values as what you want.
Consequently, if you want to change the color of rectangle repeatably, I think you can surely make the loop with this two parameters.
How to remove all vertical and horizontal lines that form boxes/tables
I have searched and tried.. But can't make it work
Have tried to search for it the last couple of days.. have found a few examples which doesn't work.. Have tried to get the pieces together..
cv:Mat img = cv::imread(input, CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat grad;
cv::Mat morphKernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
cv::morphologyEx(img, grad, cv::MORPH_GRADIENT, morphKernel);
cv::Mat res;
cv::threshold(grad, res, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// find contours
cv::Mat mask = cv::Mat::zeros(res.size(), CV_8UC1);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(res, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
for(int i = 0; i < contours.size(); i++){
cv::Mat approx;
double peri = cv::arcLength(contours[i], true);
cv::approxPolyDP(contours[i], approx, 0.04 * peri, true);
int num_vertices = approx.rows;
if(num_vertices == 4){
cv::Rect rect = cv::boundingRect(contours[i]);
// this is a rectangle
}
}
You could try something like that :
threshold your image
compute connected components
remove particules for which at least 3 of 4 bounding box tops are in touch with particule
This should give you something like that :
Here is the associated source code :
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <limits>
using namespace cv;
struct BBox {
BBox() :
_xMin(std::numeric_limits<int>::max()),
_xMax(std::numeric_limits<int>::min()),
_yMin(std::numeric_limits<int>::max()),
_yMax(std::numeric_limits<int>::min())
{}
int _xMin;
int _xMax;
int _yMin;
int _yMax;
};
int main()
{
// read input image
Mat inputImg = imread("test3_1.tif", IMREAD_GRAYSCALE);
// create binary image
Mat binImg;
threshold(inputImg, binImg, 254, 1, THRESH_BINARY_INV);
// compute connected components
Mat labelImg;
const int nbComponents = connectedComponents(binImg, labelImg, 8, CV_32S);
// compute associated bboxes
std::vector<BBox> bboxColl(nbComponents);
for (int y = 0; y < labelImg.rows; ++y) {
for (int x = 0; x < labelImg.cols; ++x) {
const int curLabel = labelImg.at<int>(y, x);
BBox& curBBox = bboxColl[curLabel];
if (curBBox._xMin > x)
curBBox._xMin = x;
if (curBBox._xMax < x)
curBBox._xMax = x;
if (curBBox._yMin > y)
curBBox._yMin = y;
if (curBBox._yMax < y)
curBBox._yMax = y;
}
}
// parse all labels
std::vector<bool> lutTable(nbComponents);
for (int i=0; i<nbComponents; ++i) {
// check current label width
const BBox& curBBox = bboxColl[i];
if (curBBox._xMax - curBBox._xMin > labelImg.cols * 0.3)
lutTable[i] = false;
else
lutTable[i] = true;
}
// create output image
Mat resImg(binImg);
MatConstIterator_<int> iterLab = labelImg.begin<int>();
MatIterator_<unsigned char> iterRes = resImg.begin<unsigned char>();
while (iterLab != labelImg.end<int>()) {
if (lutTable[*iterLab] == true)
*iterRes = 1;
else
*iterRes = 0;
++iterLab;
++iterRes;
}
// write result
imwrite("resImg3_1.tif", resImg);
}
I simply remove all labels for which with is greater than 30% of image total width. Your image is quite noisy so I can't use bounding box tops touches as said before, sorry...
Don't know if this will match with all your images but you could add some geometrical filters to improve this first version.
Regards,
You can use LineSegmentDetector for this purpose:
import numpy as np
import cv2
image = cv2.imread("image.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# This is the detector, you might have to play with the parameters
lsd = cv2.createLineSegmentDetector(0, _scale=0.6)
lines, widths, _, _ = lsd.detect(gray)
if lines is not None:
for i in range(0, len(lines)):
l = lines[i][0]
# Much slower version of Euclidean distance
if np.sqrt((l[0]-l[2])**2 + (l[1]-l[3])**2) > 50:
# You might have to tweak the threshold as well for other images
cv2.line(image, (l[0], l[1]), (l[2], l[3]), (255, 255, 255), 3,
cv2.LINE_AA)
cv2.imwrite("result.png", image)
Output:
As you can see, the lines aren't completely removed in the top image so I am leaving the tweaking part to you. Hope it helps!
I'd like to use this answer box to make a few comments.
First off, its way easier to see progress, if you can easily see what the output looks like visually. With that in mind, here is an update to your code with an emphasis on viewing interim results. I'm using VS Studio Community 2017, and OpenCV version 4.0.1 (64bit) in Win10 for anyone who wants to repeat this exercise. There were a few routines used that required updates for OpenCV 4...
#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main()
{
cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_GRAYSCALE ); // --> Contour size = 0x000000e7 hex (231 each)
// cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_REDUCED_GRAYSCALE_2); // --> Contour size = 0x00000068 hex (104 each)
// cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_REDUCED_GRAYSCALE_4); // --> Contour size = 0x0000001f hex (31 each)
// cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_REDUCED_GRAYSCALE_8); // --> Contour size = 0x00000034 hex (52 each)
if (!img.data) // Check for invalid input
{
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
// cv::namedWindow("Display Window - GrayScale Image", cv::WINDOW_NORMAL); // Create a window for display.
// cv::imshow("Display Window - GrayScale Image", img); // Show our image inside it.
// cv::waitKey(0); // Wait for a keystroke in the window
cv::Mat imgOriginal = cv::imread("0zx9Q.png", cv::IMREAD_UNCHANGED);
cv::namedWindow("Display Window of Original Document", cv::WINDOW_NORMAL); // Create a window for display.
cv::Mat grad;
cv::Mat morphKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(25, 25));
// MORPH_ELLIPSE, contourSize: 0x00000005 when 60,60... but way slow...
// MORPH_ELLIPSE, contourSize: 0x00000007 when 30,30...
// MORPH_ELLIPSE, contourSize: 0x00000007 when 20,20...
// MORPH_ELLIPSE, contourSize: 0x0000000a when 15,15...
// MORPH_ELLIPSE, contourSize: 0x0000007a when 5,5...
// MORPH_ELLIPSE, contourSize: 0x000000e7 when 3,3 and IMREAD_GRAYSCALE
// MORPH_CROSS, contourSize: 0x0000008e when 5,5
// MORPH_CROSS, contourSize: 0x00000008 when 25,25
// MORPH_RECT, contourSize: 0x00000007 when 25,25
cv::morphologyEx(img, grad, cv::MORPH_GRADIENT, morphKernel);
cv::Mat res;
cv::threshold(grad, res, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// find contours
cv::Mat mask = cv::Mat::zeros(res.size(), CV_8UC1);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(res, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
int contourSize = contours.size();
std::cout << " There are a total of " << contourSize << " contours. \n";
for (int i = 0; i < contourSize; i++) {
cv::Mat approx;
double peri = cv::arcLength(contours[i], true);
cv::approxPolyDP(contours[i], approx, 0.04 * peri, true);
int num_vertices = approx.rows;
std::cout << " Contour # " << i << " has " << num_vertices << " vertices.\n";
if (num_vertices == 4) {
cv::Rect rect = cv::boundingRect(contours[i]);
cv::rectangle(imgOriginal, rect, cv::Scalar(255, 0, 0), 4);
}
}
cv::imshow("Display Window of Original Document", imgOriginal); // Show our image inside it.
cv::waitKey(0); // Wait for a keystroke in the window
}
With that said, the parameters for getStructuringElement() matter huge. I spent a lot of time trying different choices, with very mixed results. And it turns out there are a whole lot of findContours() responses that don't have four vertices. I suspect the whole findContours() approach is probably flawed. I often would get false rectangles identified around text characters in words and phrases. Additionally the lighter lines surrounding some boxed areas would be ignored.
Instead, I think I'd be looking hard at straight line detection, via techniques discussed here, if such a response exists for a C++ and not python. Perhaps here, or here? I hoping line detection techniques would ultimately get better results. And hey, if the documents / images selected always include a white background, it would be pretty easy to solid rectangle them OUT of the image, via LineTypes: cv::FILLED
Info here provided, not as an answer to the posted question but as a methodology to visually determine success in the future.
I am trying to detect colored balls like ps3 move controller balls from 2 mt distance.I have 10 camera in same room hanging from the ceiling.Room is dark and balls have led inside.I have 4-5 balls.(red,blue,green,yellow,pink). I want track their position with opencv.Whats the right mehtod for doing this in opencv ? Can u give link , example for this ?
I use this code but i have delay problem.When i comment // my trackFilteredObject line there is no lag.But when using this code i have lot latency.I cant understand why happening because my normal cpu usage ~%15 ram usage 6.3GB/15GB (%40) when run this code cpu usage ~20-23 ram usage 6.4GB . I think its not about cpu-ram performance.What am i doing wrong ?
Video: https://www.youtube.com/watch?v=_BKtJpPrkO4 (You can see lag in first 10 sec.After 10 sen i comment tracking codes.)
Note:Kamerasayisi mean cameracount My Track Function:
void trackFilteredObject(Object theObject,Mat threshold,Mat HSV, Mat &cameraFeed){
//max number of objects to be detected in frame
const int FRAME_WIDTH = 5120;
const int FRAME_HEIGHT = 480;
const int MAX_NUM_OBJECTS=50;
//minimum and maximum object area
const int MIN_OBJECT_AREA = 10*10;
const int MAX_OBJECT_AREA = FRAME_HEIGHT*FRAME_WIDTH/1.5;
vector <Object> objects;
Mat temp;
threshold.copyTo(temp);
//these two vectors needed for output of findContours
vector< vector<Point> > contours;
vector<Vec4i> hierarchy;
//find contours of filtered image using openCV findContours function
findContours(temp,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE );
//use moments method to find our filtered object
double refArea = 0;
bool objectFound = false;
if (hierarchy.size() > 0) {
int numObjects = hierarchy.size();
//if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
if(numObjects<MAX_NUM_OBJECTS){
for (int index = 0; index >= 0; index = hierarchy[index][0]) {
Moments moment = moments((cv::Mat)contours[index]);
double area = moment.m00;
//if the area is less than 20 px by 20px then it is probably just noise
//if the area is the same as the 3/2 of the image size, probably just a bad filter
//we only want the object with the largest area so we safe a reference area each
//iteration and compare it to the area in the next iteration.
if(area>MIN_OBJECT_AREA){
Object object;
object.setXPos(moment.m10/area);
object.setYPos(moment.m01/area);
object.setType(theObject.getType());
object.setColor(theObject.getColor());
objects.push_back(object);
objectFound = true;
}else objectFound = false;
}
//let user know you found an object
if(objectFound ==true){
//draw object location on screen
drawObject(objects,cameraFeed,temp,contours,hierarchy);}
}else putText(cameraFeed,"TOO MUCH NOISE! ADJUST FILTER",Point(0,50),1,2,Scalar(0,0,255),2);
}
}
};
Main Code:
void Run()
{
int w, h;
_fps = 30;
IplImage *pCapImage[kameraSayisi];
IplImage *pDisplayImage;
PBYTE pCapBuffer = NULL;
// Create camera instance
for(int i = 0; i < kameraSayisi; i++)
{
_cam[i] = CLEyeCreateCamera(_cameraGUID[i], _mode, _resolution, _fps);
if(_cam[i] == NULL) return;
// Get camera frame dimensions
CLEyeCameraGetFrameDimensions(_cam[i], w, h);
// Create the OpenCV images
pCapImage[i] = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
// Set some camera parameters
CLEyeSetCameraParameter(_cam[i], CLEYE_GAIN, 0);
CLEyeSetCameraParameter(_cam[i], CLEYE_EXPOSURE, 511);
// Start capturing
CLEyeCameraStart(_cam[i]);
}
pDisplayImage = cvCreateImage(cvSize(w*kameraSayisi / 2, h * kameraSayisi/4 ), IPL_DEPTH_8U ,1);
if(_cam == NULL) return;
int iLastX = -1;
int iLastY = -1;
//Capture a temporary image from the camera
//program
bool trackObjects = true;
bool useMorphOps = true;
Mat HSV;
//Create a black image with the size as the camera output
Mat imgLines;
// imgLines = Mat::zeros( cvarrToMat(image).size(), CV_8UC3 );;
Mat threshold;
//x and y values for the location of the object
int x=0, y=0;
bool calibrationMode = false;
if(calibrationMode){
//create slider bars for HSV filtering
createTrackbars();
}
// image capturing loop
while(_running)
{
PBYTE pCapBuffer;
// Capture camera images
for(int i = 0; i < kameraSayisi; i++)
{
cvGetImageRawData(pCapImage[i], &pCapBuffer);
CLEyeCameraGetFrame(_cam[i], pCapBuffer, (i==0)?2000:0);
}
// Display stereo image
for(int i = 0; i < kameraSayisi; i++)
{
cvSetImageROI(pDisplayImage, cvRect(w * (i%4) ,i/4 * h, w, h));
cvCopy(pCapImage[i], pDisplayImage);
}
cvResetImageROI(pDisplayImage);
Mat imgOriginal;
Mat imgConverted = cvarrToMat(pDisplayImage);
if(calibrationMode==true)
{
//need to find the appropriate color range values
// calibrationMode must be false
//if in calibration mode, we track objects based on the HSV slider values.
//cvtColor(imgOriginal,imgOriginal,CV_BayerRG2RGB);
cvtColor(imgConverted,imgOriginal,CV_BayerGB2BGR);
cvtColor(imgOriginal,HSV,CV_BGR2HSV);
inRange(HSV,Scalar(H_MIN,S_MIN,V_MIN),Scalar(H_MAX,S_MAX,V_MAX),threshold);
morphOps(threshold);
imshow(_windowName + 'T',threshold);
//the folowing for canny edge detec
/// Create a matrix of the same type and size as src (for dst)
dst.create( imgOriginal.size(), src.type() );
/// Convert the image to grayscale
cvtColor( imgOriginal, src_gray, CV_BGR2GRAY );
/// Create a window
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
/// Create a Trackbar for user to enter threshold
// createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
/// Show the image
Object a = Object(H_MIN,S_MIN,V_MIN,H_MAX,S_MAX,V_MAX);
trackFilteredObject(a,threshold,HSV,imgOriginal);
}
else{
//we can use their member functions/information
Object blue("blue"), yellow("yellow"), red("red"), orange("orange"),white("white");
cvtColor(imgConverted,imgOriginal,CV_BayerGB2BGR);
//first find blue objects
cvtColor(imgOriginal,HSV,CV_RGB2HSV);
inRange(HSV,blue.getHSVmin(),blue.getHSVmax(),threshold);
morphOps(threshold);
//then yellows
inRange(HSV,yellow.getHSVmin(),yellow.getHSVmax(),threshold);
//then reds
inRange(HSV,red.getHSVmin(),red.getHSVmax(),threshold);
//then white
inRange(HSV,white.getHSVmin(),white.getHSVmax(),threshold);
//then orange
inRange(HSV,orange.getHSVmin(),orange.getHSVmax(),threshold);
trackFilteredObject(yellow,threshold,HSV,imgOriginal);
trackFilteredObject(white,threshold,HSV,imgOriginal);
trackFilteredObject(red,threshold,HSV,imgOriginal);
trackFilteredObject(blue,threshold,HSV,imgOriginal);
trackFilteredObject(orange,threshold,HSV,imgOriginal);
}
//delay 10ms so that screen can refresh.
//image will not appear without this waitKey() command
if (cvWaitKey(30) == 27) //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
{
cout << "esc key is pressed by user" << endl;
break;
}
// cvShowImage(_windowName, image);
imshow(_windowName,imgOriginal);
}
for(int i = 0; i < kameraSayisi; i++)
{
// Stop camera capture
CLEyeCameraStop(_cam[i]);
// Destroy camera object
CLEyeDestroyCamera(_cam[i]);
// Destroy the allocated OpenCV image
cvReleaseImage(&pCapImage[i]);
_cam[i] = NULL;
}
}
I am trying to copy one image layer to another image ROI. My code is given below.
Mat4b src= imread("path");
Mat3b dest = imread("path");
Rect roi = Rect(179,539,src.cols,src.rows); //src.cols = 1186 and src.rows= 1134 after scaling.
Mat destinationROI = dest(roi);
src.copyTo(destinationROI);
imwrite("destinationROI.png", destinationROI);
The input src is
The input dest is
But the output got is the same dest image.
Then I tried to save destinationROI befre copying. The output I got is Which is correct one. Copying the src is also working. But it does not make any effect in the dest image.
This is to confirm that #ypnos educated guess was right (nice call, btw).
Take a look at this code, that performs the same operations as yours:
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat4b m4b(50, 50, Vec4b(0, 255, 0, 255)); // blue image, 4 channels
Mat3b m3b(100, 100, Vec3b(255, 0, 0)); // green image, 3 channels
cout << "After init:" << endl;
cout << "m4b channels: " << m4b.channels() << endl;
cout << "m3b channels: " << m3b.channels() << endl << endl;
Rect roi(0, 0, 50, 50); // roi
// Create a new header for the data inside the roi in m3b
// No data copied, just a new header.
// So destRoi has same number of channels of m3b
Mat destRoi = m3b(roi);
cout << "After roi:" << endl;
cout << "m4b channels : " << m4b.channels() << endl;
cout << "m3b channels : " << m3b.channels() << endl;
cout << "destRoi channels: " << destRoi.channels() << endl << endl;
// destination type != source type
// destRoi is newly created with the destination type
// destRoi doesn't point anymore to the data in m3b and has 4 channels now
m4b.copyTo(destRoi);
cout << "After copyTo:" << endl;
cout << "m4b channels : " << m4b.channels() << endl;
cout << "m3b channels : " << m3b.channels() << endl;
cout << "destRoi channels: " << destRoi.channels() << endl << endl;
return 0;
}
Output:
After init:
m4b channels: 4
m3b channels: 3
After roi:
m4b channels : 4
m3b channels : 3
destRoi channels: 3
After copyTo:
m4b channels : 4
m3b channels : 3
destRoi channels: 4
Solution
Use both matrices with same number of channels, either by:
Load both images as 3 channels matrices CV_8UC3. In fact the images you posted are both 3 channels
use cvtColor to convert to the same number of channels, before performing roi and copy operations.
Your matrix types do not match. One has four channels, the other one three.
Obviously, copying the content of a 4-channel matrix into a 3-channel matrix can not work.
Unfortunately in some circumstances OpenCV silently falls back to creating a new matrix when it cannot work on the old memory. Most probably, this is such a case. As soon as you perform the copy, destinationROI starts pointing to new memory.
Note that you might be able to prevent this behavior by declaring destinationROI as Mat3b. To make your code work however, both source and destination need to carry the same number of channels.
Both assumptions I make here are only educated guesses though, I did not test the code in question.
your code is too initial. also your images not suit to a perfect result.guess you performed true code on the images you will get an image of who weared to shirt.
Mat4b src= imread("path");// to load 4 channel image : imread("path",IMREAD_UNCHANGED);
Mat3b dest = imread("path");
Rect roi = Rect(179,539,src.cols,src.rows); //src.cols = 1186 and src.rows= 1134 after scaling.
Mat destinationROI = dest(roi);
src.copyTo(destinationROI);
imwrite("destinationROI.png", destinationROI);
i can write a code to get following image ( it is photoshopped just to show result) but it is meaningless with your images.
EDIT 1 :
i edited your code a bit here. take a look //edit 1,2,3 inside the code.
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <stdio.h>
using namespace cv;
using namespace std;
float sx, sy;
int tx, ty;
struct TaggingPoint{
int rshx, rshy;
int lshx, lshy;
int topx, topy;
int botx, boty;
}body_points, garment_points;
int main(int argc, char** argv)
{
Mat shirt;
Mat body;
shirt = imread(argv[1], IMREAD_COLOR);
if (shirt.empty())
return -1;
body = imread(argv[2], IMREAD_COLOR);
if (body.empty())
return -1;
body_points.rshx = 418;
body_points.rshy = 706;
body_points.lshx = 1234;
body_points.lshy = 706;
body_points.topx = 838;
body_points.topy = 510;
body_points.botx = 838;
body_points.boty = 1534;
garment_points.rshx = 239;
garment_points.rshy = 147;
garment_points.lshx = 755;
garment_points.lshy = 147;
garment_points.topx = 505;
garment_points.topy = 50;
garment_points.botx = 505;
garment_points.boty = 953;
// edit 1 : when you calculate by this values your shirt image's width will be greater that body image's width
// so to reduce it
sx = (float)(body_points.lshx - body_points.rshx) / (garment_points.lshx - garment_points.rshx)*0.995;
sy = (float)(body_points.boty - body_points.topy) / (garment_points.boty - garment_points.topy);
//scale the image
resize(shirt, shirt, Size(), sx, sy, INTER_LINEAR);
imwrite("shirt.png", shirt);
//translation happened
// tx = body_points.rshx - (sx * garment_points.rshx);
tx = body_points.rshx - (garment_points.rshx);
ty = body_points.rshy - (sy * garment_points.rshy);
//draw one image over another image
//src.copyTo(dst.rowRange(1, 6).colRange(3, 10));
// shirt.copyTo(body.rowRange(tx, shirt.rows).colRange(ty, shirt.cols));
// shirt.copyTo(body.rowRange(100, 1000).colRange(100, 500));
// cvtColor(shirt, shirt, CV_BGRA2BGR);
// cvtColor(body, body, CV_BGRA2BGR);
namedWindow("body");
//Rect roi(cv::Point(tx, ty), Size(shirt.size()));
// Edit 2 : Rect.x = 0
Rect roi = Rect(0,ty,shirt.cols,shirt.rows);
Mat destinationROI = body(roi);
// cvtColor(destinationROI, destinationROI, CV_BGRA2BGR);
// Edit 3 : Create a mask ( it is show purpose only, need improvement )
Mat mask;
cvtColor( shirt, mask, COLOR_BGR2GRAY );
mask = mask < 250;
shirt.copyTo(destinationROI,mask);
imwrite("destinationROI.png", destinationROI);
imshow("body", body);
imwrite("body.png", body);
waitKey();
return 0;
}
I'm working on Opencv Project, I'm using C++. I am stuck at computing moving objects distance and speed.
Problem:
I have detected moving cars in video using Haar classifier in Opencv.
But didn't find any way to calculate moving objects
distance from camera as well as moving objects speed.
I want to achieve this using only 1 Camera. I am not using Stereo vision.
Here is my code:
#include <stdio.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>
CvHaarClassifierCascade *cascade;
CvMemStorage *storage;
void detect(IplImage *img);
int main(int argc, char** argv)
{
CvCapture *capture;
IplImage *frame;
int input_resize_percent = 100;
if(argc < 3)
{
std::cout << "Usage " << argv[0] << " cascade.xml video.avi" << std::endl;
return 0;
}
if(argc == 4)
{
input_resize_percent = atoi(argv[3]);
std::cout << "Resizing to: " << input_resize_percent << "%" << std::endl;
}
cascade = (CvHaarClassifierCascade*) cvLoad(argv[1], 0, 0, 0);
storage = cvCreateMemStorage(0);
capture = cvCaptureFromAVI(argv[2]);
assert(cascade && storage && capture);
cvNamedWindow("video", 1);
IplImage* frame1 = cvQueryFrame(capture);
frame = cvCreateImage(cvSize((int)((frame1->width*input_resize_percent)/100) , (int)((frame1->height*input_resize_percent)/100)), frame1->depth, frame1->nChannels);
const int KEY_SPACE = 32;
const int KEY_ESC = 27;
int key = 0;
do
{
frame1 = cvQueryFrame(capture);
if(!frame1)
break;
cvResize(frame1, frame);
detect(frame);
key = cvWaitKey(10);
if(key == KEY_SPACE)
key = cvWaitKey(0);
if(key == KEY_ESC)
break;
}while(1);
cvDestroyAllWindows();
cvReleaseImage(&frame);
cvReleaseCapture(&capture);
cvReleaseHaarClassifierCascade(&cascade);
cvReleaseMemStorage(&storage);
return 0;
}
void detect(IplImage *img)
{
CvSize img_size = cvGetSize(img);
CvSeq *object = cvHaarDetectObjects(
img,
cascade,
storage,
1.1, //1.1,//1.5, //-------------------SCALE FACTOR
1, //2 //------------------MIN NEIGHBOURS
0, //CV_HAAR_DO_CANNY_PRUNING
cvSize(0,0),//cvSize( 30,30), // ------MINSIZE
img_size //cvSize(70,70)//cvSize(640,480) //---------MAXSIZE
);
std::cout << "Total: " << object->total << " cars" << std::endl;
for(int i = 0 ; i < ( object ? object->total : 0 ) ; i++)
{
CvRect *r = (CvRect*)cvGetSeqElem(object, i);
cvRectangle(img,
cvPoint(r->x, r->y),
cvPoint(r->x + r->width, r->y + r->height),
CV_RGB(255, 0, 0), 2, 8, 0);
}
cvShowImage("video", img);
}
If you have any example please provide for better understanding. Its appreciated.
Thanks
I doubt about accuracy, but below mentioned way can help you to find distance of object(moving) to some extent.
Steps:
Find the suitable network to feed your frames for identifying the objects in the video.
Get coordinates of the detections and probably we could find length/area of the detection in further.
Before doing all this, find the focal length of the camera with an object of known size placed at known distance.(Refer Adrian pyimagesearch website).
With known focal length, and the moving object of known size, we can find the distance of the object while moving with the change in area of detected frame.
use this code
I have done it in android
static constexpr int32_t cam_number = 1; /**< The number of the camera, the 0 is the built in my computer. */
static constexpr int32_t cam_width = 640; /**< Width of the video's resolution. */
static constexpr int32_t cam_height = 480; /**< Height of the video's resolution. */
static constexpr int32_t threshold_min = 245; /**< Minimum value of the binary threshold. */
static constexpr int32_t threshold_max = 255; /**< Maximum value of the binary threshold. */
Look-up table for linear interpolation. If you want to make your own version, you have to re-measure these values.
static std::vector<double> pixel = {42.0, 94.0, 122.0, 139.0, 150.0, 157.0, 163.0, 168.0, 171.0}; /**< Measured values of pixels. */
static std::vector<double> cm = {20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}; /**< Measured values of centimeters. */
/* Initialize the video formats. */
cv::Mat video=srcImg;
cv::Mat video_gray=resultImage;
cv::Mat video_black_white;
try {
/* Vectors for contours and hierarchy. */
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
/* Get a new frame from the camera, convert it to grayscale, then make into black&white with binary threshold. */
cv::cvtColor(video, video_gray, cv::COLOR_RGB2GRAY);
cv::threshold(video_gray, video_black_white, threshold_min, threshold_max,
cv::THRESH_BINARY);
/* Get contours with full hierararchy and simple approximation. */
cv::findContours(video_black_white, contours, hierarchy, cv::RETR_TREE,
cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
/* If there are no contours, skip everything, otherwise there would be an exception. */
if (contours.size()) {
/* Get moments. */
cv::Moments m = cv::moments(contours[0]);
/* Protection from divison by zero. */
if (m.m00 > 0.0) {
/* Calculate the x,y coordinates of the laser point. */
double coord_x = m.m10 / m.m00;
double coord_y = m.m01 / m.m00;
/* Make sure, that we are in the look-up table's range. */
if ((coord_y > pixel[0]) && (coord_y < pixel[pixel.size() - 1])) {
/* Find the place of the coordinate in the look-up table. */
uint32_t i = 0;
while (coord_y > pixel[i + 1]) {
i++;
}
/* Calculate the value with linear interpolation. */
double distance = cm[i] + ((coord_y - pixel[i]) * (cm[i + 1] - cm[i]) /
(pixel[i + 1] - pixel[i]));
dscmm=distance;
std::cout << "X: " << coord_x << "\tY: " << coord_y << "\tDistance: "
// << distance << "\n";
/* Draw a circle on the laser and put a text with the distance on it. */
cv::circle(video, cv::Point(coord_x, coord_y), 5, cv::Scalar(0, 0, 0), 1,
8);
cv::putText(video, std::to_string(distance), cv::Point(coord_x, coord_y),
cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 0.5, cv::Scalar(255, 0, 0), 1);
}
}
}
/* Show the picture. */
/* Press any key to exit. */
}
/* Write out if there is an error. */
catch (std::exception &e) {
LOGD( "JNI:", e.what());
}