Ask Your Question
0

Detection of Black Pixels in Video

asked 2015-03-02 07:43:19 -0600

margaridamfo.gomes gravatar image

updated 2015-03-02 07:46:29 -0600

Hi everyone!

I still have problems with the property FPS, because I can't get it autocatically.

[avi @ 0x8e14540] time base not set
Could not open the output video for write: /home/margarida/Output.avi

Additionally, my goal is only to find colour frames, i.e. I need to remove all the frames constituted with black pixels from my input videos. And I get the error:

Segmentation fault (core dumped)

Does anyone detects any error? Or as a solution for my problems? Thanks in advance.

My code is:

int main(int argc, char* argv[]) {
    VideoCapture cap;
    VideoWriter outputVideo;
    string name;
    Mat frame;
    int j;

    for (j = 1; j < argc; j++) {

        printf("%d: %s\n", j, argv[j]);

        string str1 = argv[j];
        unsigned found = str1.find_last_of("/");
        name = str1.substr(0, found);
        string newname = name + "/Output.avi";
        cout << newname << '\n';

        cap.open(argv[j]);
        if (!cap.isOpened()) {
            cout << "Could not open the input video: " << argv[j] << endl;
            return -1;
        }

        //codec type
        int ex = CV_FOURCC('X', 'V', 'I', 'D');
        //transform from int to char via Bitwise operators
        char EXT[] = { (char) (ex & 0XFF), (char) ((ex & 0XFF00) >> 8),
                (char) ((ex & 0XFF0000) >> 16),
                (char) ((ex & 0XFF000000) >> 24), 0 };

        cout << "Input Codec: " << EXT << endl;

        //acquire input size
        Size S = Size((int) cap.get(CV_CAP_PROP_FRAME_WIDTH),
                (int) cap.get(CV_CAP_PROP_FRAME_HEIGHT));

        //get fps
        //double fps = cap.get(CV_CAP_PROP_FPS);

        // Open the output
        //outputVideo.open(newname, ex, fps, S, true);
        outputVideo.open(newname, ex, 13, S, true);

        if (!outputVideo.isOpened()) {
            cout << "Could not open the output video for write: " << newname
                    << endl;
            return -1;
        }

        while (true) {
            cap >> frame;
            if (frame.empty()) {
                cerr << "didnt catch frame" << endl;
                break;
            }

            for (int i = 0; i < frame.cols; i++) {
                for (int j = 0; j < frame.rows; j++) {

                    //if all the pixels != 0 [black] - clone frame
                    if ((frame.at < Vec3b > (i, j).val[0] != 0)
                            && (frame.at < Vec3b > (i, j).val[1] != 0)
                            && (frame.at < Vec3b > (i, j).val[2] != 0)) {

                        Mat frame1 = frame.clone();

                        imshow("Output", frame1);
                        out.write(frame1);

                    }
                }
            }
        }

        return 0;
    }
edit retag flag offensive close merge delete

Comments

Like being said before, the cap.get(CV_CAP_PROP_FPS) will only work if the capture interface behind it has a proper covering of that function and if your camera supports grabbing the value through its interface. If not, than this will become impossible. You could simple run a first time over your video, reading in the frames by pointer and perform the FPS calculation yourself.

StevenPuttemans gravatar imageStevenPuttemans ( 2015-03-02 08:25:30 -0600 )edit

So do something like total frames / total time of my video? And then do the rest?

margaridamfo.gomes gravatar imagemargaridamfo.gomes ( 2015-03-02 08:37:56 -0600 )edit

Yeah simple loop like this

// start timer
while ( grab a frame ){
   // check if a frame is valid
   // increase counter
}
// end timer
// divide amount of frames by timer
StevenPuttemans gravatar imageStevenPuttemans ( 2015-03-02 08:41:46 -0600 )edit

Thanks :) I finally realized what you already tried to tell me. Any suggestion on the other thing?

margaridamfo.gomes gravatar imagemargaridamfo.gomes ( 2015-03-02 08:48:22 -0600 )edit

Basically for each frame you will have to count how many pixels differ from black. If that value is larger than 0 then you keep the frame, else you remove it.

StevenPuttemans gravatar imageStevenPuttemans ( 2015-03-02 09:22:01 -0600 )edit
1

just an optimization hint, you can replace the checking pixels value loop with just calling the countnonzero() function.

theodore gravatar imagetheodore ( 2015-03-02 09:40:36 -0600 )edit

But my frames have 3 channels. So I'll need to call the countnonzero() function for every channel?

I tried to set a timer. Like http://stackoverflow.com/questions/22148826/measure-opencv-fps but it doesn't return a correct fps.

margaridamfo.gomes gravatar imagemargaridamfo.gomes ( 2015-03-02 10:11:30 -0600 )edit

yup you either call countnonzero() for each channel or you can combine it with the inRange() (i.e. threshold the black color) function, create a mask and then call the countnonzero() on the mask.

theodore gravatar imagetheodore ( 2015-03-02 10:28:23 -0600 )edit

Is something like this correct?

            split(frame, bgrChannels); // split multi-channels into single-channel

            if (countNonZero(bgrChannels[0]) > 0
                    && countNonZero(bgrChannels[1]) > 0
                    && countNonZero(bgrChannels[2]) > 0) {
                black_frames = frame.clone();
                namedWindow("OutputVideo", WINDOW_AUTOSIZE);
                imshow("OutputVideo", black_frames);
                outputVideo.write(black_frames);
            }
margaridamfo.gomes gravatar imagemargaridamfo.gomes ( 2015-03-02 11:04:03 -0600 )edit

Using the mask and the function inRange() it doesn't return anything.

And using my solution even when the frame is black, it's cloned.

margaridamfo.gomes gravatar imagemargaridamfo.gomes ( 2015-03-02 11:51:59 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
1

answered 2015-03-02 11:24:08 -0600

theodore gravatar image

updated 2015-03-02 15:52:43 -0600

edit: I transformed it to an answer since I could not edit my comment. Now it should be ok.

just go with the inRange() function since it is more convenient:

Mat mask;
cv::Scalar lowerb = cv::Scalar(0, 0, 0);
cv::Scalar upperb = cv::Scalar(0, 0, 0);
cv::inRange(frame, lowerb, upperb, mask); // if the frame has any black pixel, this will be painted in the mask as white

imshow("mask", mask); // show where black pixels are located, visualization reason only
if(countNonZero(mask) == 0) // no black pixels we keep the frame
{
   cout << "frame does not have black pixels!!!!" << endl;
   // do whatever you want with the frame
}else{
    cout << "frame has black pixels!!!" << endl;
    // skip frame
}
edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2015-03-02 07:43:19 -0600

Seen: 589 times

Last updated: Mar 02 '15