/*
* Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package boofcv.alg.background.stationary;
import boofcv.alg.background.BackgroundModelStationary;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.alg.misc.ImageMiscOps;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.ImageBase;
import boofcv.struct.image.ImageType;
import boofcv.testing.BoofTesting;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import static org.junit.Assert.assertEquals;
/**
* @author Peter Abeles
*/
public abstract class GenericBackgroundModelStationaryChecks {
Random rand = new Random(234);
int width = 60;
int height = 50;
protected List<ImageType> imageTypes = new ArrayList<>();
public abstract<T extends ImageBase>
BackgroundModelStationary<T> create( ImageType<T> imageType );
/**
* Basic check were multiple images are feed into the algorithm and another image,
* which has a region which is clearly different is then segmented.
*/
@Test
public void basicCheck() {
for( ImageType type : imageTypes ) {
basicCheck(type);
}
}
private <T extends ImageBase> void basicCheck( ImageType<T> imageType ) {
BackgroundModelStationary<T> alg = create(imageType);
T frame = imageType.createImage(width,height);
for (int i = 0; i < 30; i++) {
noise(100, 2, frame);
alg.updateBackground(frame);
}
int x0 = 10, y0 = 12, x1 = 40, y1 = 38;
noise(100,2,frame);
GImageMiscOps.fillRectangle(frame, 200, x0, y0, x1 - x0, y1 - y0);
GrayU8 segmented = new GrayU8(width,height);
alg.segment(frame, segmented);
// segmented.printBinary();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if( x >= x0 && x < x1 && y >= y0 && y < y1 ) {
assertEquals(1,segmented.get(x,y));
} else {
assertEquals(0,segmented.get(x,y));
}
}
}
}
/**
* Sees if reset discard the previous history in the background image
*/
@Test
public void reset() {
for( ImageType type : imageTypes ) {
reset(type);
}
}
private <T extends ImageBase>
void reset( ImageType<T> imageType ) {
T frame = imageType.createImage(width, height);
BackgroundModelStationary<T> alg = create(frame.getImageType());
GImageMiscOps.fill(frame,100);
alg.updateBackground(frame);
alg.reset();
GImageMiscOps.fill(frame,50);
alg.updateBackground(frame);
GrayU8 segmented = new GrayU8(width,height);
GrayU8 expected = new GrayU8(width,height);
// there should be no change
// if reset isn't the case then this will fail
alg.segment(frame,segmented);
BoofTesting.assertEquals(expected, segmented, 1e-8);
GImageMiscOps.fill(frame, 100);
ImageMiscOps.fill(expected, 1);
// it should be all changed. really just a sanity check
alg.segment(frame,segmented);
BoofTesting.assertEquals(expected,segmented,1e-8);
}
/**
* The user tries to segment before specifying the background
*/
@Test
public void segmentBeforeUpdateBackGround() {
for( ImageType type : imageTypes ) {
segmentBeforeUpdateBackGround(type);
}
}
private <T extends ImageBase>
void segmentBeforeUpdateBackGround( ImageType<T> imageType ) {
T frame = imageType.createImage(width, height);
BackgroundModelStationary<T> alg = create(frame.getImageType());
alg.setUnknownValue(2);
GrayU8 segmented = new GrayU8(width,height);
GrayU8 expected = new GrayU8(width,height);
ImageMiscOps.fill(expected, 2);
alg.segment(frame, segmented);
BoofTesting.assertEquals(expected, segmented, 1e-8);
}
@Test
public void checkSubImage() {
for( ImageType type : imageTypes ) {
checkSubImage(type);
}
}
private <T extends ImageBase>
void checkSubImage( ImageType<T> imageType ) {
T frame = imageType.createImage(width, height);
GrayU8 segmented = new GrayU8(width,height);
checkSubImage_process(frame, segmented);
GrayU8 expected = segmented.clone();
frame = BoofTesting.createSubImageOf(frame);
segmented = BoofTesting.createSubImageOf(segmented);
ImageMiscOps.fill(segmented,0);
checkSubImage_process(frame, segmented);
GrayU8 found = segmented.clone();
// see if both produce the same result
BoofTesting.assertEquals(expected,found,1e-8);
}
private <T extends ImageBase>
void checkSubImage_process( T frame, GrayU8 segmented)
{
rand = new Random(2345);
BackgroundModelStationary<T> alg = create(frame.getImageType());
for (int i = 0; i < 5; i++) {
noise(100, 30, frame);
alg.updateBackground(frame);
}
noise(100, 30, frame);
alg.segment(frame, segmented);
}
/**
* For each band in the image have all put one be filled with a constant uniform color.
* Alternate which band has motion in it.
*/
@Test
public void checkBandsUsed() {
for( ImageType type : imageTypes ) {
checkBandsUsed(type);
}
}
private <T extends ImageBase> void checkBandsUsed( ImageType<T> imageType ) {
BackgroundModelStationary<T> alg = create(imageType);
T frame = imageType.createImage(width,height);
int numBands = imageType.getNumBands();
for (int band = 0; band < numBands; band++) {
alg.reset();
for (int i = 0; i < 30; i++) {
noiseBand(100, 2, frame, band);
alg.updateBackground(frame);
}
GrayU8 segmented = new GrayU8(width,height);
// segment with the current frame. should be no motion
alg.segment(frame, segmented);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
assertEquals(0,segmented.get(x,y));
}
}
// now the whole image should report motion
noiseBand(200, 2, frame, band);
alg.segment(frame, segmented);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
assertEquals(1,segmented.get(x,y));
}
}
}
}
protected void noiseBand( double mean , double range , ImageBase image , int band ) {
double pixel[] = new double[ image.getImageType().getNumBands() ];
Arrays.fill(pixel,10);
for (int y = 0; y < image.height; y++) {
for (int x = 0; x < image.width; x++) {
pixel[band] = mean + rand.nextDouble()*2*range-range;
GeneralizedImageOps.setM(image,x,y,pixel);
}
}
}
protected void noise( double mean , double range , ImageBase image ) {
GImageMiscOps.fill(image, mean);
GImageMiscOps.addUniform(image, rand, -range, range);
}
}