title: Image shearing OpenCV CPP
body: <p>I'm trying to use cv::warpAffine to perform image transformations, but I have an issue.</p>
  <p>Note: I already saw <a href="https://stackoverflow.com/questions/46998895/image-shearing-c">questions/46998895</a> and it works well too, but with no interpolation and I wanted to use affine matrixes.</p>
  <p>So I want to shear the image and the result goes in a bigger image to have the entire sheared image like this:</p>
  <p><a href="https://i.stack.imgur.com/KFk5Y.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/KFk5Y.jpg" alt="photo1" /></a></p>
  <p>Right now, my idea was to perform the shear on inverted image if the values are negatives rather than calculate the new image size with negative values etc as seen on <a href="https://blog.paperspace.com/data-augmentation-for-object-detection-rotation-and-shearing/" rel="nofollow noreferrer">paperspace blog</a>.</p>
  <blockquote>
  <p>When we use a negative shear, the direction of shear is right to left, while x2 is not further in the negative direction than x1. One way to solve this could be to get the other set of corners (that would satisfy the constraint, can you prove it?). Apply the shear transformation and then change to the other set of corners because of the notation we follow.
  Well, we could do that, but there's a better method. Here's how how perform a negative shear with shearing factor -alpha.</p>
  <ol>
  <li>Flip the image and boxes horizontally.</li>
  <li>Apply the positive shear transformation with shearing factor alpha</li>
  <li>Flip the image and boxes horizontally again.</li>
  </ol>
  </blockquote>
  <p>The left ones are my results, and the right ones are the expected results :</p>
  <p><a href="https://i.stack.imgur.com/5IOsy.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/5IOsy.jpg" alt="results" /></a></p>
  <p>So as you can see it works well when x and y are both positive values or when one is 0 and the other is negative, its ok. When one is positive and the other is negative, or when they are both negative, it provides always the same result for my shear function, see the 4 last lines in the pic above. Again, the left forms are my results, the right ones are the <code>mickaShear()</code> results (and obviously the correct ones).</p>
  <p>My question is: what do I have to do in my shear_matrix to perform the correct transformation with negative values? Is the <code>cv::flip()</code> method described by <a href="https://blog.paperspace.com/data-augmentation-for-object-detection-rotation-and-shearing/" rel="nofollow noreferrer">paperSpace</a> the right way to achieve this transformation?</p>
  <p>Here you can find the reproductible code with the comparison between my function and the function in <a href="https://stackoverflow.com/a/47042795/16600459">this answer</a>. <strong>You will need OpenCV</strong>. I work on OpenCV 4.5.1</p>
  <pre><code>#include &lt;opencv2/core/mat.hpp&gt;
  #include &lt;opencv2/highgui.hpp&gt;
  #include &lt;opencv2/features2d.hpp&gt;
  #include &lt;opencv2/xfeatures2d.hpp&gt;
  #include &lt;opencv2/imgproc.hpp&gt;
  #include &lt;opencv2/core.hpp&gt;
  #include &lt;iostream&gt;
  
  cv::Mat myShear(const cv::Mat &amp; src, float sx, float sy) {
      cv::Mat tmp;
      cv::Mat dst;
  
      std::vector&lt;cv::Point2f&gt; extremePoints;
      extremePoints.emplace_back(cv::Point2f(0, 0));
      extremePoints.emplace_back(cv::Point2f((float)src.cols, 0));
      extremePoints.emplace_back(cv::Point2f((float)src.cols, (float)src.rows));
      extremePoints.emplace_back(cv::Point2f(0, (float)src.rows));
      for(auto &amp; pt : extremePoints){
          pt = cv::Point2f(pt.x + pt.y * abs(sx), pt.y + pt.x * abs(sy));
      }
      cv::Rect offsets = cv::boundingRect(extremePoints);
      cv::Size new_size = offsets.size();
      float mat_values[] = {1.0f, abs(sx), 0.0f, abs(sy), 1.0f, 0.0f};
      cv::Mat shear_matrix = cv::Mat(2, 3, CV_32F, mat_values);
      dst = cv::Mat::zeros(new_size, src.type());
  
      /*
       *cv::flip(img, tmp, INT_FLIP_CODE) where INT_FLIP_CODE can be: 
       * 0   (Vertically) 
       * 1   (Horizontally)
       * -1  (Both)
       */
      if(sx &lt; 0.0f and sy &lt; 0.0f) {
          cv::flip(img, tmp, -1);
          cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
          cv::flip(dst, dst, -1);
      } else if(sx &lt; 0.0f) {
          cv::flip(img, tmp, 1);
          cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
          cv::flip(dst, dst, 1);
      } else if(sy &lt; 0.0f) {
          cv::flip(img, tmp, 0);
          cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
          cv::flip(dst, dst, 0);
      } else {
          tmp = src.clone();
          cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
      }
      return dst;
  }
  
  cv::Mat mickaShear(const cv::Mat &amp; input, float Bx, float By)
  {
      if (Bx*By == 1)
      {
          std::cerr &lt;&lt; &quot;error == 1&quot; &lt;&lt; std::endl;
      }
      if (input.type() != CV_8UC3) return {};
  
      std::vector&lt;cv::Point2f&gt; extremePoints;
      extremePoints.emplace_back(0, 0);
      extremePoints.emplace_back(input.cols, 0);
      extremePoints.emplace_back(input.cols, input.rows);
      extremePoints.emplace_back(0, input.rows);
      for (auto &amp; pt : extremePoints)
      {
          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());
  
      for (int j = 0; j &lt; shearedImage.rows; ++j){
          for (int i = 0; i &lt; shearedImage.cols; ++i){
              cv::Point2f pp((float)i, (float)j);
              pp = pp - offset; // go back to original coordinate system
              cv::Point2f p;
              p.x = int((-pp.y*Bx + pp.x) / (1 - By*Bx));
              p.y = int(pp.y - p.x*By);
              if ((p.x &gt;= 0 &amp;&amp; p.x &lt; (float)input.cols) &amp;&amp; (p.y &gt;= 0 &amp;&amp; p.y &lt; (float)input.rows)){
                  shearedImage.at&lt;cv::Vec3b&gt;(j, i) = input.at&lt;cv::Vec3b&gt;(p);
              }
          }
      }
      return shearedImage;
  }
  
  int main(int argc, char *argv[]){
      float x = -0.2f; //CHANGE SIGN TO TEST
      float y = 0.5f; //CHANGE SIGN TO TEST
  
      cv::Mat im = cv::imread(&quot;MODIFY BY JPG FILE PATH&quot;);
      cv::Mat result = im.clone();
      cv::Mat output = cv::Mat();
      for(auto &amp; aug: augments){
          output = mickaShear(im, x, y);
          result = myShear(im);
      }
      cv::imshow(&quot;result&quot;, result);
      cv::imshow(&quot;output&quot;, output);
      auto k = cv::waitKey(0);
      if(k == (int)'q'){
          cv::destroyAllWindows();
          break;
      }
      cv::destroyAllWindows();
      return 0;
  }
  </code></pre>