Poul K. Sørensen
Poul K. Sørensen

Reputation: 17530

OpenCV Integer division in c++ not as expected

Given the following output:

[11233, 11345, 11434, 10897] [44, 44, 45, 43] [-31, 81, -86, -111]

from this code

std::cout << mat32sc1;
channels[1] = mat32sc1 / 256;
channels[0] = mat32sc1 - channels[1] * 256;
std::cout << channels[1];
std::cout << channels[0];

I would have expected 11233 / 256 to be 43, using integer division?

Is my assumption wronge that c++ always do integer division by flooring?

Update

Here is my current encoding function.

void encode(cv::Mat & src, cv::Mat & dst)
{
    cv::Mat_<int> mat32sc1;
    src.convertTo(mat32sc1, CV_32SC1, 10, 11000);

    std::vector<cv::Mat> channels;
    channels.resize(3);

//  bitwise_and(mat32sc1, cv::Scalar(255), channels[0]); // is this needed or will converTo truncate automaticly.
//  channels[0].convertTo(channels[0], CV_8UC1);
//  mat32sc1.convertTo(channels[1], CV_8UC1, 1.0 / (1 << 8));
    channels[2] = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
    int flag = 256;
//  std::cout << mat32sc1;
    channels[1] = mat32sc1 / flag;
    channels[0] = mat32sc1 - channels[1] * flag;
    cv::Mat_<int> off = (channels[0] < 0) / 255;
    //std::cout << off;
    channels[1] -= off;
    channels[0] = mat32sc1 - channels[1] * flag;


    //std::cout << channels[1];
    //std::cout << channels[0];
    channels[0].convertTo(channels[0], CV_8UC1);
    channels[1].convertTo(channels[1], CV_8UC1);

    cv::merge(channels, dst);
}

Any smarter way to get the same result

Upvotes: 6

Views: 1305

Answers (4)

Phillip Boone
Phillip Boone

Reputation: 21

I think a simple vector solution for integer division (without rounding) would be to drop the same amount of bits for the desired bit shift before doing the division. For example, I'm using CV_16U and trying to shift right by 4 bits. I mask off first by 0xFFF0 first:

// Bit shift right 4 bits with floor function for input "frameMat" CV_16U

cv::Mat frameMatMasked;

cv::bitwise_and(frameMat, 0xFFF0, frameMatMasked);

frameMatMasked.convertTo(shiftedMat, CV_8U, (1.0 / 16.0));

Upvotes: 0

S. Huerta
S. Huerta

Reputation: 11

Here is a hand-made function to perform integer division on OpenCV, based on your ideas. Hope it's helpful.

#include "opencv2\highgui.hpp"
using cv::Mat;
#include "opencv2\imgproc.hpp"
using cv::threshold;
void IntegerDivide(Mat& Input_Dividendo, int Input_Divisor, Mat& Output_Cociente, Mat& Output_Resto)
{
    // Input_Dividendo is supposed to be CV_32S
    // Input_Dividendo = Input_Divisor * Output_Cociente + Output_Resto

    Mat candidato_Cociente=Input_Dividendo/Input_Divisor;
    Mat offset=Input_Dividendo-(candidato_Cociente*Input_Divisor);
    Mat error_en_candidato_Cociente;

    // Threshold
    // threshold( offset, error_en_candidato_Cociente, 0, 1,CV_THRESH_BINARY_INV);
    Mat aux_src;
    offset.convertTo(aux_src,CV_32F);
    Mat aux_dst;
    threshold( aux_src, aux_dst, 0, 1,CV_THRESH_BINARY_INV);
    aux_dst.convertTo(error_en_candidato_Cociente,CV_32S);  
    // End threshold

    Output_Cociente=candidato_Cociente-error_en_candidato_Cociente;

    Output_Resto=Input_Dividendo-(Output_Cociente*Input_Divisor);
}

Upvotes: 0

Michael Burdinov
Michael Burdinov

Reputation: 4428

The division is indeed not an integer division. Most functions in OpenCV convert their input to Scalar, which is container for 1, 2, 3, or 4 doubles. And other functions in OpenCV that perform similar operations (scaleAdd, addWeighted, convertTo, etc...) all works with doubles as well. In other words, your code performing division by double and rounding of the result. This is why you got 44 instead of 43.

Edit:

As for the "encode" function you don't need to perform complex operations. The bytes for your new matrix are already there. You just need to create convenient way of accessing them:

Mat temp(src.size(), CV_8UC4, src.data);

This will create new matrix header (i.e. no copy of data) that point to the data of src. But instead of seeing the data as integer matrix with single channel, it will be 4-channel matrix of unsigned char (with same width and height). Than you can do anything with multi-channel matrix: split(), merge(), mixChannels(), etc...

Upvotes: 3

Fytch
Fytch

Reputation: 1087

No, you're right, integer division is performed by flooring in C++. See [expr.mul]:

For integral operands the / operator yields the algebraic quotient with any fractional part discarded; 81

81) This is often called truncation towards zero.

Upvotes: 2

Related Questions