background extraction and update from video using matlab - image-processing

I have a video about traffic scene. Now, I want to calculate the percentage of vehicle area on the road area (or the percentage of foreground area on the background area). The first step for this is background extraction. I have read many document and scientific articles about it, one of them recommends to use the mean filter following this formula:
This is the link of that the article. The results are very good, it is exactly what I want.
I followed his formula and I tried to write my code. But It didn't work! Who can help me and give me some advice.
This is my code:
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
imtool close all; % Close all imtool figures.
clear; % Erase all existing variables.
workspace; % Make sure the workspace panel is showing.
fontSize = 14;
%input video;
step = 10;
vob = VideoReader('NKKN.avi');
frame = vob.read(inf);
vidHeight = vob.Height;
vidWidth = vob.Width;
nFrames = vob.NumberOfFrames;
%%% First-iteration background frame
background_frame = double(frame*0);
redbackground_frame = background_frame(:,:,1);
greenbackground_frame = background_frame(:,:,2);
bluebackground_frame = background_frame(:,:,3);
%calculate background
i = 0;
for k = 1:10 %get background from 10 frame (J=10)
thisframe = double(read(vob, k));
%background_frame = background_frame + thisframe;
redbackground_frame = redbackground_frame + thisframe(:,:,1);
greenbackground_frame = greenbackground_frame + thisframe(:,:,2);
bluebackground_frame = bluebackground_frame + thisframe(:,:,3);
i=i+1;
disp(i);
end
A = redbackground_frame/i;
B = greenbackground_frame/i;
C = bluebackground_frame/i;
background = cat(3,A,B,C);
imshow(background);

You can maintain a buffer of B frames for a dynamic estimation of backgound
buff = NaN( [vidHeight, vidWidth, 3, B] ); % allocate room for buffer
% process the video
for fi = 1:nFrames
% read current frame
thisframe = double(read(vob, k)) / 255; % convert to [0..1] range
% update background model
buff(:, :, :, mod( fi, B ) + 1 ) = thisframe;
background_L1 = nanmedian( buff, 4 ); % I think this is better than `mean` - try it!
background_L2 = nanmean( buff, 4 );
% do whatever processing you need with fi-th frame
% and the current background mode...
% ...
end
Note that if fi < B (i.e., you processed less than B frames) the background model is not stable. I am using NaNs as default values for the buffer and these values are ignored when backgound model is estimated -- this is the reason why I use nanmedian and nanmean instead of simply median and mean.

vob = VideoReader('NKKN.avi');
frame = vob.read(inf);
vidHeight = vob.Height;
vidWidth = vob.Width;
nFrames = vob.NumberOfFrames;
%allocate room for buffer of 20 frames
buff = NaN( [vidHeight, vidWidth, 3, 20] ); % allocate room for buffer
for fi = 1:20:nFrames
disp(fi);
% read current frame
thisframe = double(read(vob, fi)) / 255; % convert to [0..1] range
% update background model
buff(:, :, :, mod( fi, 10 ) + 1 ) = thisframe;
background_L1 = nanmedian( buff, 4 );
background_L2 = nanmean( buff, 4 );
hImage = subplot(2, 2, 1);
image(thisframe);
caption = sprintf('thisframe');
title(caption, 'FontSize', fontSize);
drawnow; % Force it to refresh the window.
subplot(2,2,2);
imshow(background_L2);
title('background-L2');
subplot(2,2,3);
imshow(background_L1);
title('background-L1');
end

Extracting the background of this video
https://www.youtube.com/watch?v=URJxS1giCA4&ab_channel=AliShahzil
clear all
close all
reader = VideoReader('C:\Users\Ali Sahzil\Desktop\Media.wmv'); // your video file location
vid = {};
while hasFrame(reader)
vid{end+1} = im2single(readFrame(reader));
end
bg = mean( cat(4, vid{:}), 4);
x =bg;
imshow( bg );

Here is a very simple solution you can build upon. First you will need a sample background image of the scene with no traffic. We will call this 'bg'.
Here is a simple approach in pseudo-code:
load in background image 'bg'
set threshold upper value
set threshold lower value
loop until done for each frame
{
subtract 'bg' image from your first frame
if pixel value of foreground > than threshold upper value
{
set foreground pixel value to 'nan'
}
if pixel value of foreground < than threshold lower value
{
set foreground pixel value to 'nan'
}
if pixel value of foreground == 0
{
set foreground pixel value to 'nan'
}
}
This will bracket your foreground images to only show the parts of the scene you are interested in. Note: this process can be greatly enhanced by using a stereoscopic camera to give you depth perception. However, you should be able to build upon this code to remove unwanted parts of your image.

This is actually based on Shai's and user3725204's answers. I didn't use read and NumberOfFrames which are not recommended. I also adopted user3725204's suggestion, since there's no need to read all frames.
function backGrnd = getBackGrnd(filename, nTest, method)
tic
if nargin < 2, nTest = 20; end
if nargin < 3, method = 'median'; end
v = VideoReader(filename);
nChannel = size(readFrame(v), 3);
tTest = linspace(0, v.Duration-1/v.FrameRate , nTest);
%allocate room for buffer
buff = NaN([v.Height, v.Width, nChannel, nTest]);
for fi = 1:nTest
v.CurrentTime =tTest(fi);
% read current frame and update model
buff(:, :, :, mod(fi, nTest) + 1) = readFrame(v);
end
switch lower(method)
case 'median'
backGrnd = nanmedian(buff, 4);
case 'mean'
backGrnd = nanmean(buff, 4);
end
toc
end
And the result is like this:
subplot(221); imshow(uint8(TrafficVisionLab.getBackGrnd('traffic.avi', 10, 'mean')));
subplot(222); imshow(uint8(TrafficVisionLab.getBackGrnd('traffic.avi', 10, 'median')));
subplot(223); imshow(uint8(TrafficVisionLab.getBackGrnd('traffic.avi', 50, 'mean')));
subplot(224); imshow(uint8(TrafficVisionLab.getBackGrnd('traffic.avi', 50, 'median')));

Related

How to detect a personmovement in a RoI

I have a video and want to know when a person enters and stays in a specific area in the video, then sets a time in the video (video time), when he enters, and when he leaves to use this times later for cutting the video.
Im only a little bit experienced in opencv but have currently no experience with tensorflow or keras.
This is for a video analysis.
I have tried some things like BackgroundSubtractorMOG, use another resolution, etc.
https://s18.directupload.net/images/190517/wym8r59b.png
https://s18.directupload.net/images/190517/pi52vgv7.png
def calc_accum_avg(frame, accumulated_weight):
global background
if background is None:
background = frame.copy().astype("float")
return None
cv2.accumulateWeighted(frame, background, accumulated_weight)
def segment(frame, threshold=25):
global background
diff = cv2.absdiff(background.astype("uint8"),frame)
_, thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresholded.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) == 0:
return None
else:
move_segment = max(contours, key = cv2.contourArea)
return (thresholded, move_segment)
def main():
video = cv2.VideoCapture("/home/felix/Schreibtisch/OpenCVPython/large_video.mp4")
video.set(3, 1920)
video.set(4, 1080)
length = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
print(length)
num_frames = 0
fgbg = cv2.bgsegm.createBackgroundSubtractorMOG()
while True:
ret,frame = video.read()
fgmask = fgbg.apply(frame)
if frame is None:
return
frame_copy = fgmask.copy()
#frame2_copy =frame.copy()
roi_visualiser = frame[roi_visualiser_top:roi_visualiser_bottom,roi_visualiser_right:roi_visualiser_left]
roi_board = frame[roi_board_top:roi_board_bottom,roi_board_right:roi_board_left]
gray = cv2.cvtColor(roi_visualiser, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (9,9), 0)
#gray = cv2.cvtColor(roi_board, cv2.COLOR_BGR2GRAY)
#gray = cv2.GaussianBlur(gray, (9,9), 0)
if num_frames < 2:
calc_accum_avg(gray, accumulated_weight)
#calc_accum_avg(gray2, accumulated_weight)
if num_frames <= 1:
cv2.imshow("Finger Count", frame_copy)
else:
hand = segment(gray)
if hand is not None:
thresholded, move_segment = hand
cv2.drawContours(frame_copy, [move_segment + (roi_visualiser_right, roi_visualiser_top)], -1, (255,0,0), 1)
#cv2.drawContours(frame_copy2, [move_segment + (roi_board_right, roi_board_top)], -1, (255,0,0), 1)
fingers = count_moves(thresholded, move_segment)
if fingers > 0:
print("ja") #test funktioniert
else:
print("Nein")
cv2.putText(frame_copy, str(fingers), (70,45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2) #no need
cv2.imshow("Thresholded", thresholded) #no need
cv2.rectangle(frame_copy, (roi_visualiser_left, roi_visualiser_top), (roi_visualiser_right, roi_visualiser_bottom), (255,0,0), 1)
cv2.rectangle(frame_copy, (roi_board_left, roi_board_top), (roi_board_right, roi_board_bottom), (255,0,0), 1)
num_frames += 1
cv2.imshow("Finger Count", frame_copy)
I get no error messages all runs fine, but i dont get the correct result i need.
[1]: https://i.stack.imgur.com/dQbQi.png
[2]: https://i.stack.imgur.com/MqOAc.png
Have you tried BackgroundSubtractorMOG2? It can distinguish shadows, you can use that to prevent false positives.
To make the processing more efficient, first create a subimage of the area where the person enters/leaves. Apply the backgroundsubtraction on the subimage. Also, if the frames are noisy, applying a blur before backgroundsubtraction can improve the result.
Check the resulting mask for white objects of significant size. If detected, store the frame number using video.get(CV_CAP_PROP_POS_FRAMES) in an array and stop recording frame numbers until the mask is fully black again.

Generating a Histogram by Harmonic Number

I am trying to create a program in GNU Octave to draw a histogram showing the fundamental and harmonics of a modified sinewave (the output from an SCR dimmer, which consists of a sinewave which is at zero until part way through the wave).
I've been able to generate the waveform and perform FFT to get a set of Frequency vs Amplitude points, however I am not sure how to convert this data into bins suitable for generating a histogram.
Sample code and an image of what I'm after below - thanks for the help!
clear();
vrms = 120;
freq = 60;
nCycles = 2;
level = 25;
vpeak = sqrt(2) * vrms;
sampleinterval = 0.00001;
num_harmonics = 10
disp("Start");
% Draw the waveform
x = 0 : sampleinterval : nCycles * 1 / freq; % time in sampleinterval increments
dimmed_wave = [];
undimmed_wave = [];
for i = 1 : columns(x)
rad_value = x(i) * 2 * pi * freq;
off_time = mod(rad_value, pi);
on_time = pi*(100-level)/100;
if (off_time < on_time)
dimmed_wave = [dimmed_wave, 0]; % in the dimmed period, value is zero
else
dimmed_wave = [dimmed_wave, sin(rad_value)]; % when not dimmed, value = sine
endif
undimmed_wave = [undimmed_wave, sin(rad_value)];
endfor
y = dimmed_wave * vpeak; % calculate instantaneous voltage
undimmed = undimmed_wave * vpeak;
subplot(2,1,1)
plot(x*1000, y, '-', x*1000, undimmed, '--');
xlabel ("Time (ms)");
ylabel ("Voltage");
% Fourier Transform to determine harmonics
subplot(2,1,2)
N = length(dimmed_wave); % number of points
fft_vals = abs(fftshift(fft(dimmed_wave))); % perform fft
frequency = [ -(ceil((N-1)/2):-1:1) ,0 ,(1:floor((N-1)/2)) ] * 1 / (N *sampleinterval);
plot(frequency, fft_vals);
axis([0,400]);
xlabel ("Frequency");
ylabel ("Amplitude");
You know your base frequency (fundamental tone), let's call it F. 2*F is the second harmonic, 3*F the third, etc. You want to set histogram bin edges halfway between these: 1.5*F, 2.5*F, etc.
You have two periods in your input signal, therefore your (integer) base frequency is k=2 (the value at fft_vals[k+1], the first peak in your plot). The second harmonic is at k=4, the third one at k=6, etc.
So you would set your bins edges at k = 1:2:end.
In general, this would be k = nCycles/2:nCycles:end.
You can compute your bar graph according to our computed bin edges as follows:
fft_vals = abs(fft(dimmed_wave));
nHarmonics = 9;
edges = nCycles/2 + (0:nHarmonics)*nCycles;
H = cumsum(fft_vals);
H = diff(H(edges));
bar(1:nHarmonics,H);

OpenCV Tone Curve progrommatically

I want to realize smth like tone curve.
I have predefined set of curves that I should apply to the image.
For instance:
as I understand on this chart we see dependences of current tone value to new, for example:
if we get first dot on the left - every r,g and b that = 0 will be converted to 64
or every value more than 224 will be converted to 0 and ect.
so I tried to change every pixel of image to new value
for test purpose i've simplified curve:
and here the code I have:
//init original image
cv::Mat originalMat = [self cvMatFromUIImage:inputImage];
//out image the same size
cv::Mat outMat = [self cvMatFromUIImage:inputImage];
//loop throw every row of image
for( int y = 0; y < originalMat.rows; y++ ){
//loop throw every column of image
for( int x = 0; x < originalMat.cols; x++ ){
//loop throw every color channel of image (R,G,B)
for( int c = 0; c < 3; c++ ){
if(originalMat.at<cv::Vec3b>(y,x)[c] <= 64)
outMat.at<cv::Vec3b>(y,x)[c] = 64 + ( originalMat.at<cv::Vec3b>(y,x)[c] ) -
( originalMat.at<cv::Vec3b>(y,x)[c] ) * 2 ;
if((originalMat.at<cv::Vec3b>(y,x)[c] > 64)&&(originalMat.at<cv::Vec3b>(y,x)[c] <= 128))
outMat.at<cv::Vec3b>(y,x)[c] = (( originalMat.at<cv::Vec3b>(y,x)[c] ) - 64 ) * 4
;
if((originalMat.at<cv::Vec3b>(y,x)[c] > 128))
outMat.at<cv::Vec3b>(y,x)[c] = ( originalMat.at<cv::Vec3b>(y,x)[c] ) + 128 -
(( originalMat.at<cv::Vec3b>(y,x)[c] ) - 128) * 3;
} //end of r,g,b loop
} //end of column loop
} //end of row loop
//send to output
return [self UIImageFromCVMat:outMat];
but here the result I get:
by some reason only 3/4 of image was processed
and it not matches with result i expected:
Update 0
thanks to #ACCurrent comment found errors in calculation(code and image updated), but still not understand why only 3/4 of images processed.
not sure that understand why 'noise' appears, hope it because of curve not smooth.
looks the way to avoid .at operation.
Update 1
original image:
You need to access the images with Vec4b
originalMat.type() is equals to 24
Your originalMat is of type 24, i.e. CV_8UC4. This means that the image has 4 channels, but you're accessing it with Vec3b as if it has only 3 channels. This explains why about 1/4 of the image is not modified.
So, simply replace every Vec3b in your code with Vec4b.

Creating an image of difference of adjacent pixels with digitalmicrograph (DM) script

The following digitalmicrograph function tries to create an image by taking difference of neighboring pixel in a sub-row of a row of the image. The first pixel is replaced with a mean of the difference result of the sub-row thus created.
E.g. If the input image is 8 pixels wide and 1 rows tall and the sub-row size is 4 -
In_img = {8,9,2,4,9,8,7,5}
Then the output image will be -
Out_img = {mean(8,9,2,4)=5.75,9-8=1,2-9=-7,4-2=2,mean(9,8,7,5)=7.25,8-9=-1,7-8=-1,5-7=-2}
When I run this script, the first pixel of the first row is correct but rest of the pixels are incorrect. When I set the loop limit to only one sub-row and one row i.e. x=1 and y=1, then the script works correctly.
Any ideas as to what may be happening or what may be wrong with the script?
The test image is here and the result is here.
// Function to compute the standard deviation (sigma n-1) of an image, or
// a set of values passed in as pixel values in an image. The
// number of data points (n) the mean and the sum are also returned.
// version:20080229
// D. R. G. Mitchell, adminnospam#dmscripting.com (remove the nospam to make this email address work)
// v1.0, February 2008
void StandardDeviation(image arrayimg, number &stddev, number &n, number &mean, number &sum)
{
mean=mean(arrayimg)
number xsize, ysize
getsize(arrayimg,xsize, ysize)
n=xsize*ysize
sum=sum(arrayimg)
image imgsquared=arrayimg*arrayimg
number sumofvalssqrd=sum(imgsquared)
stddev=sqrt(((n*sumofvalssqrd)-(sum*sum))/(n*(n-1)))
}
image getVectorImage(image refImage, number rowsize)
{
number fh, fv, fhx
getsize(refImage, fh, fv)
fhx=trunc(fh/rowsize)
//result("ByteSize of refimage = "+refImage.ImageGetDataElementByteSize()+"\n")
//create image to save std of each row of the ref image.
//The std values are saved as pixels of one row. The row size is same as number of rows.
//use fhx*rowsize for the new imagesize as fhx is truncated value.
image retImage:=RealImage("",4,fhx*rowsize,fv)
image workImage=slice1(refImage,rowsize+1,0,0,0,rowsize-1,1)
number stddev,nopix,mean,sum
for ( number y=0;y<fv;y++)
{
for (number x=0;x<fhx;x++)
{
//result ("x,y="+x+","+y+"; fhx="+fhx+"; rowsize="+rowsize+"\n")
workImage=slice1(refImage,x*rowsize+1,y,0,0,rowsize-1,1)-slice1(refImage,x*rowsize,y,0,0,rowsize-1,1)
showimage(workImage)
StandardDeviation(workImage,stddev,nopix,mean,sum )
retImage[y,x*rowsize+1,y+1,x*rowsize+rowsize]=workImage
retImage[y,x]=mean
result("mean # row "+y+" = "+mean+"\n")
}
}
return retImage
}
showimage(getVectorImage(getfrontimage(),rowsize))
After your edit, I understood that you want to do something like this:
and that this should be performed for each line of the image individually.
The following script does this. (Explanations below.)
image Modify( image in, number subsize )
{
// Some checking
number sx,sy
in.GetSize(sx,sy)
if ( 0 != sx%subsize )
Throw( "The image width is not an integer multiplication of the subsize." )
// Do the means...
number nTile = sx/subsize
image meanImg := RealImage( "Means", 4, nTile , sy )
meanImg = 0
for ( number i=0; i<subsize; i++ )
meanImg += in.Slice2( i,0,0, 0,nTile,subsize, 1,sy,1 )
meanImg *= 1/subsize
// Do the shifted difference
image dif := RealImage( "Diff", 4, sx-1, sy )
dif = in.slice2( 1,0,0, 0,sx-1,1, 1,sy,1) - in.slice2( 0,0,0, 0,sx-1,1, 1,sy,1)
// Compile the result
image out := in.ImageClone()
out.SetName( in.getName() + "mod" )
out.slice2( 1,0,0, 0,sx-1,1, 1,sy,1 ) = dif
out.slice2( 0,0,0, 0,nTile,subsize, 1,sy,1 ) = meanImg
return out
}
number sx = 8, sy = 4
image img := RealImage( "test", 4, 8, 4 )
img = icol*10 + trunc( Random()*10 )
img.ShowImage()
Modify(img,4).ShowImage()
Some explanations:
You want to do two different things in the image, so you have to be careful not to overwrite data in pixels you will subsequently use for computation! Images are processed pixel by pixel, so if you first compute the mean and write it in the first pixel, the evaluation of the second pixel will be the difference of "9" and the just stored mean-value (not the original "8"). So you have to split computation and use "buffer" copies.
The slice2 command is extremely convenient, because it allows to define a stepsize when sampling. You can use it to address the dark-grey pixels directly.
Be aware of the difference between := and = in image expressions. The first is a memory assignment:
A := B means that A now is the same memory location as B. A is basically another name for B.
A = B means A gets the values of B (copied). A and B are two different memory locations and only values are copied over.
I have some observations in your script:
Instead of the defined method for getting mean/sum/stdev/n of an image, you can as easily get to those numbers from any image img using the following:
mean: number m = mean( img )
sum: number s = sum( img )
stdev: number sd = sqrt( variance( img ) )
pixels: number n = sum( 0 * img + 1 )
if you want to get the difference of an image with an image "shifted by one" you don't have to loop over the lines/columns but can directly use the slice2() command; a [ ] notation using icol and irow; or the command offset() Personally, I prefer the slice2() command.
If I want a script which gives me the standard deviation of the difference of each row with its successor row, i.e. stdDev( row_(y) - row_(y+1) ) for all y < sizeY, my script would be:
Image img := GetFrontImage()
number sx,sy
img.GetSize(sx,sy)
number dy = 1
Image dif = img.Slice2(0,0,0, 0,sx,1, 1,sy-1,1 ) - img.Slice2(0,dy,0, 0,sx,1, 1,sy-1,1)
Image sDevs := RealImage( "Row's stDev", 4, sy-1 )
for ( number y=0; y<sy-1; y++ )
sDevs[y,0] = SQRT( Variance( dif.Slice1(0,y,0, 0,sx,1) ) )
sDevs.ShowImage()
Is this, what you try to achieve? If not, please edit your question for some clarification.

How do I draw a segment of a circle in Lua in Corona?

long time ago is last time I needed that :)
I simple like to create a circle with a segment and a different filling or transparent.
So i just have like a stopwatch filling up the circle by time (60 seconds).
function kind a > showsegment (xcircle,ycircle,radius, seconds) :}#
any short lines leading to that solution, are welcome.
The code needs to work within the Corona Framework, in Lua.
I don't think you can. Using image with alpha and tint is not an option?
Yes, you'll have to create 60 objects, one for every tick, but images are cached anyway, so you only loading it and allocating memory for it once. Every next instance is cheap.
I am not sure if this is what you're looking for, but seeing the question it made me curious, so I messed around with it and figured this (if it's what you're looking for):
tick = 0;
ticks = {};
cr = 250; -- Circle radius
hr = 0.9; -- hand radius
hw = 10; -- hand width
mr = 0.25; -- middle radius (fg)
bg = display.newCircle(cr, cr, cr); -- background
for i=1,360 do
local w = hr * (cr * 2);
local x = (w/2)+(((cr*2) - w)/2);
local t = display.newRect(x,x,hw,w);
t:rotate(i-1);
t:setFillColor(0,0, 0);
table.insert(ticks, t);
end
function drawTick(e)
print("tick "..tick);
local dg = display.newGroup();
local w = hr * (cr * 2);
local x = (w/2)+(((cr*2) - w)/2);
local t = display.newRect(dg, 0, -w/4, 10, w/2);
dg.x = x;
dg.y = x;
t:setFillColor(0, 1, 0);
dg:rotate(tick-1);
table.insert(ticks, t);
fg = display.newCircle(cr,cr,mr*cr);
if tick < 361 then
tick = tick + 1
timer.performWithDelay(50, drawTick);
end
end
timer.performWithDelay(0, drawTick);
EDIT: I cleaned up the code a bit.

Resources