[Solved] Image Shearing C++


I found some time to work on this.
Now I understand what you tried to achieve with the offset computation, but I’m not sure whether yours is correct.

Just change all the cv::Vec3b to unsigned char or uchar and load as grayscale, if wanted.

Please try this code and maybe you’ll find your error:

// no interpolation yet
// cv::Vec3b only
cv::Mat shear(const cv::Mat & input, float Bx, float By)
{
    if (Bx*By == 1)
    {
        throw("Shearing: Bx*By==1 is forbidden");
    }


    if (input.type() != CV_8UC3) return cv::Mat();

    // shearing:
    // x'=x+y·Bx
    // y'=y+x*By

    // shear the extreme positions to find out new image size:
    std::vector<cv::Point2f> extremePoints;
    extremePoints.push_back(cv::Point2f(0, 0));
    extremePoints.push_back(cv::Point2f(input.cols, 0));
    extremePoints.push_back(cv::Point2f(input.cols, input.rows));
    extremePoints.push_back(cv::Point2f(0, input.rows));

    for (unsigned int i = 0; i < extremePoints.size(); ++i)
    {
        cv::Point2f & pt = extremePoints[i];
        pt = cv::Point2f(pt.x + pt.y*Bx, pt.y + pt.x*By);
    }

    cv::Rect offsets = cv::boundingRect(extremePoints);

    cv::Point2f offset = -offsets.tl();
    cv::Size resultSize = offsets.size();

    cv::Mat shearedImage = cv::Mat::zeros(resultSize, input.type()); // every pixel here is implicitely shifted by "offset"

    // perform the shearing by back-transformation
    for (int j = 0; j < shearedImage.rows; ++j)
    {

        for (int i = 0; i < shearedImage.cols; ++i)
        {
            cv::Point2f pp(i, j);

            pp = pp - offset; // go back to original coordinate system

            // go back to original pixel:
            // x'=x+y·Bx
            // y'=y+x*By
            //   y = y'-x*By
            //     x = x' -(y'-x*By)*Bx 
            //     x = +x*By*Bx - y'*Bx +x'
            //     x*(1-By*Bx) = -y'*Bx +x'
            //     x = (-y'*Bx +x')/(1-By*Bx)

            cv::Point2f p;
            p.x = (-pp.y*Bx + pp.x) / (1 - By*Bx);
            p.y = pp.y - p.x*By;

            if ((p.x >= 0 && p.x < input.cols) && (p.y >= 0 && p.y < input.rows))
            {
                // TODO: interpolate, if wanted (p is floating point precision and can be placed between two pixels)!
                shearedImage.at<cv::Vec3b>(j, i) = input.at<cv::Vec3b>(p);
            }
        }
    }

    return shearedImage;
}


int main(int argc, char* argv[])
{
    cv::Mat input = cv::imread("C:/StackOverflow/Input/Lenna.png");

    cv::Mat output = shear(input, 0.7, 0);
    //cv::Mat output = shear(input, -0.7, 0);
    //cv::Mat output = shear(input, 0, 0.7);

    cv::imshow("input", input);
    cv::imshow("output", output);

    cv::waitKey(0);
    return 0;
}

Giving me these outputs for the 3 sample lines:

enter image description here

enter image description here

enter image description here

solved Image Shearing C++