Heey, I'm trying to sort out the function of Optical Flow of openCV, but for some reason I'm getting an exception in visual studio:
Unhandled exception at 0x772615de in Optical_flow.exe: Microsoft C++ exception: cv::Exception at memory location 0x0036f334..
With breakpoints I found out that the error lies within the cvCalcOpticalFlowHS function.
I'm using openCV 2.1
#include <cv.h>
#include <highgui.h>
using namespace cv;
int init() {
return 0;
}
int main(int argc, char **args) {
CvCapture* capture = cvCaptureFromFile("Video/Wildlife.wmv");
double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
CvSize size;
size.width = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
size.height = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
CvVideoWriter* writer = cvCreateVideoWriter("result.avi", 0, fps,size, 1);
IplImage* curFrame = cvQueryFrame(capture);
Mat u = Mat(size, CV_32FC2);
Mat v = Mat(size, CV_32FC2);
CvTermCriteria IterCriteria;
IterCriteria.type = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;
IterCriteria.max_iter = 500;
IterCriteria.epsilon = 0.01;
while(1) {
IplImage* nextFrame = cvQueryFrame(capture);
if(!nextFrame) break;
u = Mat::zeros(size, CV_32FC2);
v = Mat::zeros(size, CV_32FC2);
/* Do optical flow computation */
cvCalcOpticalFlowHS(&curFrame, &nextFrame, 0, &u, &v, 0.01, IterCriteria);
cvWriteFrame(writer, curFrame);
curFrame = nextFrame;
}
cvReleaseVideoWriter(&writer);
cvReleaseCapture(&capture);
return 0;
}
Anyone has seen this problem before or sees the mistake I made?
Best Regards
Remco
From the documentation, curFrame and nextFrame should be 8-bit single channel. You are currently just pulling these from the loaded file without checking/converting them as necessary. Can you confirm that the input is of the right type?
Also you have a nasty mix of C++ style cv::Mat with C style IplImage*. I'd suggest you upgrade to a more recent version of OpenCV (2.4 has recently been released), and try to stick with the one or other of the C++ or C style methods.
Note also that this optical flow method is classed as obsolete with a recommendation to use either calcOpticalFlowPyrLK() for sparse features or calcOpticalFlowFarneback() for dense features.
Below is some example code demonstrating calcOpticalFlowFarneback(), which is what I believe you are trying to achieve. It takes data from the webcam rather than a file.
#include <opencv2/opencv.hpp>
using namespace cv;
void drawOptFlowMap(const cv::Mat& flow,
cv::Mat& cflowmap,
int step,
const cv::Scalar& color
)
{
for(int y = 0; y < cflowmap.rows; y += step)
for(int x = 0; x < cflowmap.cols; x += step)
{
const cv::Point2f& fxy = flow.at<cv::Point2f>(y, x);
cv::line(cflowmap,
cv::Point(x,y),
cv::Point(cvRound(x+fxy.x),cvRound(y+fxy.y)),
color);
cv::circle(cflowmap, cv::Point(x,y), 2, color, -1);
}
}
int main(int argc, char **args) {
VideoCapture cap(0); // open the default camera
if(!cap.isOpened()) // check if we succeeded
return -1;
Mat newFrame, newGray, prevGray;
cap >> newFrame; // get a new frame from camera
cvtColor(newFrame, newGray, CV_BGR2GRAY);
prevGray = newGray.clone();
double pyr_scale = 0.5;
int levels = 3;
int winsize = 5;
int iterations = 5;
int poly_n = 5;
double poly_sigma = 1.1;
int flags = 0;
while(1) {
cap >> newFrame;
if(newFrame.empty()) break;
cvtColor(newFrame, newGray, CV_BGR2GRAY);
Mat flow = Mat(newGray.size(), CV_32FC2);
/* Do optical flow computation */
calcOpticalFlowFarneback(
prevGray,
newGray,
flow,
pyr_scale,
levels,
winsize,
iterations,
poly_n,
poly_sigma,
flags
);
drawOptFlowMap(flow, newFrame, 20, CV_RGB(0,255,0));
namedWindow("Output",1);
imshow("Output", newFrame);
waitKey(1);
prevGray = newGray.clone();
}
return 0;
}
The above code is pretty similar to the fback.cpp sample code which comes with OpenCV.
Related
I want to apply unsharp mask like Adobe Photoshop,
I know this answer, but it's not as sharp as Photoshop.
Photoshop has 3 parameters in Smart Sharpen dialog: Amount, Radius, Reduce Noise; I want to implement all of them.
This is the code I wrote, according to various sources in SO.
But the result is good in some stages ("blurred", "unsharpMask", "highContrast"), but in the last stage ("retval") the result is not good.
Where am I wrong, what should I improve?
Is it possible to improve the following algorithm in terms of performance?
#include "opencv2/opencv.hpp"
#include "fstream"
#include "iostream"
#include <chrono>
using namespace std;
using namespace cv;
// from https://docs.opencv.org/3.4/d3/dc1/tutorial_basic_linear_transform.html
void increaseContrast(Mat img, Mat* dst, int amountPercent)
{
*dst = img.clone();
double alpha = amountPercent / 100.0;
*dst *= alpha;
}
// from https://stackoverflow.com/a/596243/7206675
float luminanceAsPercent(Vec3b color)
{
return (0.2126 * color[2]) + (0.7152 * color[1]) + (0.0722 * color[0]);
}
// from https://stackoverflow.com/a/2938365/7206675
Mat usm(Mat original, int radius, int amountPercent, int threshold)
{
// copy original for our return value
Mat retval = original.clone();
// create the blurred copy
Mat blurred;
cv::GaussianBlur(original, blurred, cv::Size(0, 0), radius);
cv::imshow("blurred", blurred);
waitKey();
// subtract blurred from original, pixel-by-pixel to make unsharp mask
Mat unsharpMask;
cv::subtract(original, blurred, unsharpMask);
cv::imshow("unsharpMask", unsharpMask);
waitKey();
Mat highContrast;
increaseContrast(original, &highContrast, amountPercent);
cv::imshow("highContrast", highContrast);
waitKey();
// assuming row-major ordering
for (int row = 0; row < original.rows; row++)
{
for (int col = 0; col < original.cols; col++)
{
Vec3b origColor = original.at<Vec3b>(row, col);
Vec3b contrastColor = highContrast.at<Vec3b>(row, col);
Vec3b difference = contrastColor - origColor;
float percent = luminanceAsPercent(unsharpMask.at<Vec3b>(row, col));
Vec3b delta = difference * percent;
if (*(uchar*)&delta > threshold) {
retval.at<Vec3b>(row, col) += delta;
//retval.at<Vec3b>(row, col) = contrastColor;
}
}
}
return retval;
}
int main(int argc, char* argv[])
{
if (argc < 2) exit(1);
Mat mat = imread(argv[1]);
mat = usm(mat, 4, 110, 66);
imshow("usm", mat);
waitKey();
//imwrite("USM.png", mat);
}
Original Image:
Blurred stage - Seemingly good:
UnsharpMask stage - Seemingly good:
HighContrast stage - Seemingly good:
Result stage of my code - Looks bad!
Result From Photoshop - Excellent!
First of all, judging by the artefacts that Photoshop left on the borders of the petals, I'd say that it applies the mask by using a weighted sum between the original image and the mask, as in the answer you tried first.
I modified your code to implement this scheme and I tried to tweak the parameters to get as close as the Photoshop result, but I couldn't without creating a lot of noise. I wouldn't try to guess what Photoshop is exactly doing (I would definitely like to know), however I discovered that it is fairly reproducible by applying some filter on the mask to reduce the noise. The algorithm scheme would be:
blurred = blur(image, Radius)
mask = image - blurred
mask = some_filter(mask)
sharpened = (mask < Threshold) ? image : image - Amount * mask
I implemented this and tried using basic filters (median blur, mean filter, etc) on the mask and this is the kind of result I can get:
which is a bit noisier than the Photoshop image but, in my opinion, close enough to what you wanted.
On another note, it will of course depend on the usage you have for your filter, but I think that the settings you used in Photoshop are too strong (you have big overshoots near petals borders). This is sufficient to have a nice image at the naked eye, with limited overshoot:
Finally, here is the code I used to generate the two images above:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat usm(Mat original, float radius, float amount, float threshold)
{
// work using floating point images to avoid overflows
cv::Mat input;
original.convertTo(input, CV_32FC3);
// copy original for our return value
Mat retbuf = input.clone();
// create the blurred copy
Mat blurred;
cv::GaussianBlur(input, blurred, cv::Size(0, 0), radius);
// subtract blurred from original, pixel-by-pixel to make unsharp mask
Mat unsharpMask;
cv::subtract(input, blurred, unsharpMask);
// --- filter on the mask ---
//cv::medianBlur(unsharpMask, unsharpMask, 3);
cv::blur(unsharpMask, unsharpMask, {3,3});
// --- end filter ---
// apply mask to image
for (int row = 0; row < original.rows; row++)
{
for (int col = 0; col < original.cols; col++)
{
Vec3f origColor = input.at<Vec3f>(row, col);
Vec3f difference = unsharpMask.at<Vec3f>(row, col);
if(cv::norm(difference) >= threshold) {
retbuf.at<Vec3f>(row, col) = origColor + amount * difference;
}
}
}
// convert back to unsigned char
cv::Mat ret;
retbuf.convertTo(ret, CV_8UC3);
return ret;
}
int main(int argc, char* argv[])
{
if (argc < 3) exit(1);
Mat original = imread(argv[1]);
Mat expected = imread(argv[2]);
// closer to Photoshop
Mat current = usm(original, 0.8, 12., 1.);
// better settings (in my opinion)
//Mat current = usm(original, 2., 1., 3.);
cv::imwrite("current.png", current);
// comparison plot
cv::Rect crop(127, 505, 163, 120);
cv::Mat crops[3];
cv::resize(original(crop), crops[0], {0,0}, 4, 4, cv::INTER_NEAREST);
cv::resize(expected(crop), crops[1], {0,0}, 4, 4, cv::INTER_NEAREST);
cv::resize( current(crop), crops[2], {0,0}, 4, 4, cv::INTER_NEAREST);
char const* texts[] = {"original", "photoshop", "current"};
cv::Mat plot = cv::Mat::zeros(120 * 4, 163 * 4 * 3, CV_8UC3);
for(int i = 0; i < 3; ++i) {
cv::Rect region(163 * 4 * i, 0, 163 * 4, 120 * 4);
crops[i].copyTo(plot(region));
cv::putText(plot, texts[i], region.tl() + cv::Point{5,40},
cv::FONT_HERSHEY_SIMPLEX, 1.5, CV_RGB(255, 0, 0), 2.0);
}
cv::imwrite("plot.png", plot);
}
Here's my attempt at 'smart' unsharp masking. Result isn't very good, but I'm posting anyway. Wikipedia article on unsharp masking has details about smart sharpening.
Several things I did differently:
Convert BGR to Lab color space and apply the enhancements to the brightness channel
Use an edge map to apply enhancement to the edge regions
Original:
Enhanced: sigma=2 amount=3 low=0.3 high=.8 w=2
Edge map: low=0.3 high=.8 w=2
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <cstring>
cv::Mat not_so_smart_sharpen(
const cv::Mat& bgr,
double sigma,
double amount,
double canny_low_threshold_weight,
double canny_high_threshold_weight,
int edge_weight)
{
cv::Mat enhanced_bgr, lab, enhanced_lab, channel[3], blurred, difference, bw, kernel, edges;
// convert to Lab
cv::cvtColor(bgr, lab, cv::ColorConversionCodes::COLOR_BGR2Lab);
// perform the enhancement on the brightness component
cv::split(lab, channel);
cv::Mat& brightness = channel[0];
// smoothing for unsharp masking
cv::GaussianBlur(brightness, blurred, cv::Size(0, 0), sigma);
difference = brightness - blurred;
// calculate an edge map. I'll use Otsu threshold as the basis
double thresh = cv::threshold(brightness, bw, 0, 255, cv::ThresholdTypes::THRESH_BINARY | cv::ThresholdTypes::THRESH_OTSU);
cv::Canny(brightness, edges, thresh * canny_low_threshold_weight, thresh * canny_high_threshold_weight);
// control edge thickness. use edge_weight=0 to use Canny edges unaltered
cv::dilate(edges, edges, kernel, cv::Point(-1, -1), edge_weight);
// unsharp masking on the edges
cv::add(brightness, difference * amount, brightness, edges);
// use the enhanced brightness channel
cv::merge(channel, 3, enhanced_lab);
// convert to BGR
cv::cvtColor(enhanced_lab, enhanced_bgr, cv::ColorConversionCodes::COLOR_Lab2BGR);
// cv::imshow("edges", edges);
// cv::imshow("difference", difference * amount);
// cv::imshow("original", bgr);
// cv::imshow("enhanced", enhanced_bgr);
// cv::waitKey(0);
return enhanced_bgr;
}
int main(int argc, char *argv[])
{
double sigma = std::stod(argv[1]);
double amount = std::stod(argv[2]);
double low = std::stod(argv[3]);
double high = std::stod(argv[4]);
int w = std::stoi(argv[5]);
cv::Mat bgr = cv::imread("flower.jpg");
cv::Mat enhanced = not_so_smart_sharpen(bgr, sigma, amount, low, high, w);
cv::imshow("original", bgr);
cv::imshow("enhanced", enhanced);
cv::waitKey(0);
return 0;
}
I am currently working on translating Matlab version active contour to OpenCV. It seems that OpenCV has its own version of active contour but deprecated then. I am curious about how different between this version with Matlab version. So I did a comparison experiment. Given a MRI brain image and an initial boundary , I applied both Matlab and OpenCV version active contour to segment the white matter in brain.
For both Matlab and OpenCV, I choose gradient(edge) method of active contour. I then labeled the results on the image. The red curve is initial boundary while the green curve is final boundary.
The Matlab code is:
clear
clc
I = imread('brain.png');
IMaskS = imread('shrinkMask.png');
figure(1)
imshow(I)
hold on
visboundaries( IMaskS, 'color', 'r');
bwSnake = activecontour(I, IMaskS, 250, 'edge','ContractionBias',-0.45,'SmoothFactor',0.7);
finalBoundary = bwboundaries(bwSnake);
visboundaries(finalBoundary, 'color', 'g');
It seems that Matlab does a really good job when choosing the appropriate parameters. The "ContractionBias" parameter (refer it here) is really useful for controlling the curve to shrink or expand.
However, the OpenCV version active contour seems doesn't work at all. I use slider bar to change the parameters alpha, beta and gamma but cannot get a descend result. It seems that the curve doesn't expand no matter how I set the parameters. I use OpenCV 2.4.13.3 and active contour can be found in legacy folder as snakes.cpp. Chek OpenCV github or more specifically snakes.cpp. The OpenCV test code is:
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/legacy/legacy.hpp"
using namespace std;
using namespace cv;
IplImage *image = 0;
IplImage *image2 = 0;
IplImage *imageMask = 0;
using namespace std;
int ialpha = 10;
int ibeta = 40;
int igamma = 50;
void onChange(int pos)
{
if (image2) cvReleaseImage(&image2);
if (image) cvReleaseImage(&image);
if (imageMask) cvReleaseImage(&imageMask);
image2 = cvLoadImage("brain.png", 1);
image = cvLoadImage("brain.png", 0);
imageMask = cvLoadImage("shrinkMask.png", 0);
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = 0;
cvFindContours(imageMask, storage, &contours, sizeof(CvContour),
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
if (!contours) return;
int length = contours->total;
if (length<10) return;
CvPoint* point = new CvPoint[length];
CvSeqReader reader;
CvPoint pt = cvPoint(0, 0);;
CvSeq *contour2 = contours;
cvStartReadSeq(contour2, &reader);
for (int i = 0; i < length; i++)
{
CV_READ_SEQ_ELEM(pt, reader);
point[i] = pt;
}
cvReleaseMemStorage(&storage);
for (int i = 0; i<length; i++)
{
int j = (i + 1) % length;
cvLine(image2, point[i], point[j], CV_RGB(255, 0, 0), 1, 8, 0);
}
float alpha = ialpha / 100.0f;
float beta = ibeta / 100.0f;
float gamma = igamma / 100.0f;
CvSize size;
size.width = 3;
size.height = 3;
CvTermCriteria criteria;
criteria.type = CV_TERMCRIT_ITER;
criteria.max_iter = 500;
criteria.epsilon = 0.1;
cvSnakeImage(image, point, length, &alpha, &beta, &gamma, CV_VALUE, size, criteria, 1);
for (int i = 0; i<length; i++)
{
int j = (i + 1) % length;
cvLine(image2, point[i], point[j], CV_RGB(0, 255, 0), 1, 8, 0);
}
delete[]point;
}
int main(int argc, char* argv[])
{
cvNamedWindow("win1", 0);
cvCreateTrackbar("alpha", "win1", &ialpha, 100, onChange);
cvCreateTrackbar("beta", "win1", &ibeta, 100, onChange);
cvCreateTrackbar("gamma", "win1", &igamma, 100, onChange);
//cvResizeWindow("win1", 696, 520);
cvResizeWindow("win1", 256, 256);
onChange(0);
for (;;)
{
if (cvWaitKey(40) == 27) break;
cvShowImage("win1", image2);
}
return 0;
}
Is there anything I did wrong or this version of active contour in OpenCV is completely useless? I checked the snake.cpp, the source code for OpenCV version active contour, it seems that it is implemented based on the theory in paper. Because of file protection, I cannot access the Matlab version active contour. Therefore, I cannot compare them directly. Since eventually, I will use OpenCV to implement stuffs, any suggestion for improving or changing my OpenCV code will be appreciated.
I attached the original image and mask in case someone wants to repeat the test.
Thanks!
Folks,
I have a realsense SR300, but when I display my depth image in a opencv window, it looks too dark. How can I fix this? When I run the realsense examples the images look good, but the examples use OpenGL. But I need OpenCV for my projects. Here is my code:
int main(int argc, char ** argv)
{
// realsense camera setup
rs::log_to_console(rs::log_severity::warn);
// Create a context object. This object owns the handles to all connected realsense devices
rs::context ctx;
if (ctx.get_device_count() == 0)
{
throw std::runtime_error("No device detected. Is it plugged in?");
}
// Access the first available RealSense device
rs::device * dev = ctx.get_device(0);
// Configure depth to run at VGA resolution at 30 frames per second
dev->enable_stream(rs::stream::depth, 640, 480, rs::format::z16, 30);
rs::intrinsics depth_intrin;
rs::format depth_format;
depth_intrin = dev->get_stream_intrinsics(rs::stream::depth);
depth_format = dev->get_stream_format(rs::stream::depth);
cv::namedWindow("Send Display Image", CV_WINDOW_AUTOSIZE);
/* Set callbacks prior to calling start(). */
auto depth_callback = [depth_intrin, depth_format](rs::frame f)
{
cv::Mat image(cv::Size(640, 480), CV_16UC1,
(void*)f.get_data(), cv::Mat::AUTO_STEP);
cv::imshow("Send Display Image", image);
cv::waitKey(1000/80);
};
/* callback to grab depth fream and publish it. */
dev->set_frame_callback(rs::stream::depth, depth_callback);
// Start streaming
dev->start();
While(1)
{
}
return 0;
}
I am not sure why my image is so dark. I want it to look something like the kinect or the Xtion when I run openni_launch from ROS
Edit:
The normalized function below produces some flickering:
I suspect that is due to the maximal depth value flickering.
The minimal depth value is always 0 as this value is used when the depth is invalid and thus the depth range becomes false.
Instead you should use this:
void make_depth_histogram(const Mat &depth, Mat &normalized_depth) {
normalized_depth = Mat(depth.size(), CV_8U);
int width = depth.cols, height = depth.rows;
static uint32_t histogram[0x10000];
memset(histogram, 0, sizeof(histogram));
for(int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
++histogram[depth.at<ushort>(i,j)];
}
}
for(int i = 2; i < 0x10000; ++i) histogram[i] += histogram[i-1]; // Build a cumulative histogram for the indices in [1,0xFFFF]
for(int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
if (uint16_t d = depth.at<ushort>(i,j)) {
int f = histogram[d] * 255 / histogram[0xFFFF]; // 0-255 based on histogram location
normalized_depth.at<uchar>(i,j) = static_cast<uchar>(f);
} else {
normalized_depth.at<uchar>(i,j) = 0;
}
}
}
}
What you observe is because the depth stream is coded on 16 bits (rs::stream::z16) whereas when displayed only 8 bits will be used.
You can normalized your depth map:
double min, max;
minMaxLoc(depth, &min, &max);
Mat depth_normalized;
double alpha = 255.0/(max-min);
depth.convertTo(depth_normalized, CV_8U, alpha, -min*alpha);
Or use a kind of colormap to display the depth: make_depth_histogram().
Full demo code:
inline void make_depth_histogram(const Mat &depth, Mat &color_depth) {
color_depth = Mat(depth.size(), CV_8UC3);
int width = depth.cols, height = depth.rows;
static uint32_t histogram[0x10000];
memset(histogram, 0, sizeof(histogram));
for(int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
++histogram[depth.at<ushort>(i,j)];
}
}
for(int i = 2; i < 0x10000; ++i) histogram[i] += histogram[i-1]; // Build a cumulative histogram for the indices in [1,0xFFFF]
for(int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
if (uint16_t d = depth.at<ushort>(i,j)) {
int f = histogram[d] * 255 / histogram[0xFFFF]; // 0-255 based on histogram location
color_depth.at<Vec3b>(i,j) = Vec3b(f, 0, 255 - f);
} else {
color_depth.at<Vec3b>(i,j) = Vec3b(0, 5, 20);
}
}
}
}
int main(int argc, char *argv[]) {
// Create a context object. This object owns the handles to all connected realsense devices
rs::context ctx;
// Access the first available RealSense device
rs::device * dev = ctx.get_device(0);
// Configure Infrared stream to run at VGA resolution at 30 frames per second
dev->enable_stream(rs::stream::depth, 640, 480, rs::format::z16, 30);
// Start streaming
dev->start();
// Camera warmup - Dropped several first frames to let auto-exposure stabilize
for(int i = 0; i < 30; i++)
dev->wait_for_frames();
// Creating OpenCV Matrix from a color image
Mat depth(Size(640, 480), CV_16U, (void*)dev->get_frame_data(rs::stream::depth), Mat::AUTO_STEP);
// Create a color depth
Mat color_depth;
make_depth_histogram(depth, color_depth);
// Create a normalized depth
double min, max;
minMaxLoc(depth, &min, &max);
Mat depth_normalized;
double alpha = 255.0/(max-min);
depth.convertTo(depth_normalized, CV_8U, alpha, -min*alpha);
// Display in a GUI
imshow("Display normalized depth", depth_normalized);
imshow("Display color depth", color_depth);
waitKey(0);
return 0;
}
The only solution I found to this problem which gives satisfactory results is the following:
Save the image as a PNG file. (PNG supports saving 16-bit images)
Use matplotlib to view it in a colored map:
#!/usr/bin/python3
import numpy as np
import cv2
import sys
from matplotlib import pyplot as plt
def printCoordinates(event):
x,y = event.xdata,event.ydata
if x != None:
print("X : ",x," Y: ",y," Value = ",img[np.int(y),np.int(x)])
img = cv2.imread(sys.argv[1],cv2.CV_16UC1)
#img = img/65535
fig = plt.figure()
plt.imshow(img,cmap='nipy_spectral')
cid = fig.canvas.mpl_connect('button_press_event',printCoordinates)
plt.colorbar()
plt.show()
The button_press_event is to print the exact pixel value on the pixel clicked.
RGB Image:
Corresponding Depth Image:
I am recently working with OpenCV and C++ for a project and I found a weird thing:
when I try to access single pixal value in IplImage and assign other value, it could run properly but the result is that it can only operate part of the whole image.
relevant code:
IplImage* output_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)(current_frame->imageData+current_frame->widthStep*row);
uchar* ptr_output_frame = (uchar *)(output_frame->imageData+output_frame->widthStep*row);
for (int cols=0;cols<width;cols++)
{
//other codes...
ptr_output_frame[cols]=ptr_current_frame[cols];
}
}
}
The result is the left part of the image was copied to the output_frame. And when I run the following code:
IplImage* output_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)current_frame->imageData+current_frame->width*row;
uchar* ptr_output_frame = (uchar *)output_frame->imageData+output_frame->width*row;
for (int cols=0;cols<width;cols++)
{
//other codes...
ptr_output_frame[cols]=ptr_current_frame[cols];
}
}
}
I got the upside part of the image in the output_frame.
I cannot get the the whole image copy in output_frame either way. Could anybody help me with this? Thanks a lot!
[updates] 05/16/2015
I have found out that output_frame->widthStep is different from current->widthStep after current_frame is executed in the following code:
current_frame = cvQueryFrame(video_gray);
It makes sense why the first part of the code wouldnt work. But I still dont know why the second part of the code doesnt work.
I now have my whole codes updated here and hope you can help me make this right. I am really appreciate your help.
CvCapture* video_gray = cvCreateFileCapture("test_gray.avi");
const double fps = cvGetCaptureProperty(video_gray, CV_CAP_PROP_FPS);
const int width = (int)cvGetCaptureProperty(video_gray, CV_CAP_PROP_FRAME_WIDTH);
const int height = (int)cvGetCaptureProperty(video_gray, CV_CAP_PROP_FRAME_HEIGHT);
const CvSize size = cvSize(width, height);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* output_frame=cvCreateImage(size, IPL_DEPTH_8U, 1);
int flag = 0;
cvNamedWindow("test",1);
cvNamedWindow("test2",1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
cout<<flag++<<endl;
if(flag<500) continue;
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)(current_frame->imageData+current_frame->widthStep*row);
uchar* ptr_output_frame = (uchar *)(output_frame->imageData+output_frame->widthStep*row);
for (int cols=0;cols<width;cols++)
{
ptr_output_frame[cols]= ptr_current_frame[cols];
}
}
cvShowImage("test",output_frame);
cvShowImage("test2",current_frame);
cvWaitKey(10);
}
You don't handle the number of channels...
please try
IplImage* output_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)(current_frame->imageData+current_frame->widthStep*row);
uchar* ptr_output_frame = (uchar *)(output_frame->imageData+output_frame->widthStep*row);
for (int cols=0;cols<width;cols++)
{
//other codes...
// here insead take care of the channels too. Only write ne channel to the output
ptr_output_frame[cols]=ptr_current_frame[cols*current_frame->nChannels];
}
}
}
but you should really try to switch to C++ API: try something like this:
cv::VideoCapture video = cv::VideoCapture("test_gray.avi");
int width = ...
int height = ...
cv::Mat image_captured;
cv::Mat image_gray; // if wanted
cv::Mat image_output1 = cv::Mat(height, width, CV_8UC1);
cv::Mat image_output2 = cv::Mat(height, width, CV_8UC1);
while(video.read(image_captured))
{
// if you want to convert the image to
if(image_captured.channels() > 1)
{
cv::cvtColor(image_captured, image_gray, CV_BGR2GRAY);
}
else
{
image_gray = image_captured;
}
[...]
for(int j=0; j<height; ++j)
{
for(int i=0; i<width; ++i)
{
image_output1.at<uchar>(j,i) = image_gray.at<uchar>(j,i); // read from single channel image
image_output1.at<uchar>(j,i) = image_captured.at<cv::Vec3b>(j,i)[0]; // only read the first channel of a multi-channel image
}
}
}
cv::imshow("output1", image_output1);
cv::imshow("output2", image_output2);
cv::imshow("input", image_capured);
cv::waitKey(0);
}
much easier to use, and you can still make it more efficient by using row-pointers in each iteration etc
I am trying to use the cvGoodFeatureToTrack function in Visual Studio 2010 with the image type as Mat. Most of the examples I have seen use the IplImage pointer.
Right now I have this:
int w, h; // video frame size
Mat grayFrame;
Mat eigImage;
Mat tempImage;
const int MAX_CORNERS = 10;
CvPoint2D32f corners[MAX_CORNERS] = {0};
int corner_count = MAX_CORNERS;
double quality_level = 0.1;
double min_distance = 10;
int eig_block_size = 3;
int use_harris = false;
w = CurrFrame.size().width;
h = CurrFrame.size().height;
cvtColor(CurrFrame, grayFrame, CV_BGR2GRAY);
cvGoodFeaturesToTrack(&grayFrame,
&eigImage,
&tempImage,
corners,
&corner_count,
quality_level,
min_distance,
NULL,
eig_block_size,
use_harris);
It compiles but gives me a memory access violation. Help!
As a starting point, if using C++ anyway (like your use of cv::Mat and cv::cvtColor suggests), then why not use C++ interface for the rest, too?
This would mean the use of cv::goodFeaturesToTrack or a cv::GoodFeaturesToTrackDetector, which are made to work on cv::Mat and friends instead of making unneccessary casts from cv::Mat to IplImage*.
cv::Mat grayFrame;
std::vector<cv::Point2f> corners;
double quality_level = 0.1;
double min_distance = 10;
int eig_block_size = 3;
int use_harris = false;
const int MAX_CORNERS = 10;
cv::cvtColor(CurrFrame, grayFrame, CV_BGR2GRAY);
cv::goodFeaturesToTrack(grayFrame,
corners,
MAX_CORNERS,
quality_level,
min_distance,
cv::noArray(),
eig_block_size,
use_harris);