QPixmap copy contents of a square drawn into another image - c++

I have an image onto which I draw a Rectangle as such. After that I am attempting to copy the contents of the rectangle onto another QLabel. This seems to work however i cannot seem to align the copied image starting from the left top corner of the image. Here is what I am doing
QPixmap original_image;
original_image.load("c:\\Images\\myimg.jpg");
original_image = original_image.scaled(ui.label->size().width(),ui.label->size().height());
//-----------------------------------------------------------------------
//Draw rectangle on this
QPixmap target_two(ui.label->size().width(),ui.label->size().height());
target_two.fill(Qt::transparent);
QPixmap target(ui.label->size().width(),ui.label->size().height());
target.fill(Qt::transparent);
QPainter painter(&target);
QPainter painter_two(&target_two);
QRegion r(QRect(0, 0, ui.label->size().width(), ui.label->size().height()), QRegion::RegionType::Rectangle); //Region to start copying
painter.setClipRegion(r);
painter.drawPixmap(0, 0, original_image); //Draw the original image in the clipped region
QRectF rectangle(x_start,y_start,clipRegion);
painter.drawRoundedRect(rectangle,0,0); //Last two parameters define the radius of the corners higher the radius more rounded it is
QRegion r_two(rectangle.toRect(), QRegion::RegionType::Rectangle);
painter_two.setClipRegion(r_two);
painter_two.drawPixmap(0,0,target);
ui.label->setPixmap(target);
ui.label_2->setPixmap(target_two);
The bottom picture is the image with the red rectangle in it and that is fine.
The top picture is a copy of the contents of the square.The only problem is its not starting from the top left corner.
Any suggestion on why I am not getting the copied content on the top left corner.

The problem in your logic is that both target and target_two images have the same sizes - size of the label, and you draw the copied image in the same position as it was in the initial label. So far so good. I would solve this by the following code:
[..]
// This both lines can be removed.
// QRegion r_two(rectangle.toRect(), QRegion::RegionType::Rectangle);
// painter_two.setClipRegion(r_two);
// Target rect. in the left top corner.
QRectF targetRect(0, 0, rectangle.width(), rectangle.height());
QRectF sourceRect(rectangle);
// Draw only rectangular area of the source image into the new position.
painter_two.drawPixmap(targetRect, target, sourceRect);
[..]

Related

Rounded corners of a QLabel containing a pixmap

I am trying to display an Image inside a QLabel on QT forms. I need that label to have only the top left and right corners to be rounded while the bottom 2 remain rectangular.
using style sheets I gave the border-radius a value and it worked. Howerver, the image inside that label remained rectangular. (the corner of the image hid the circular corner of the QLabel).
Searching around, i found that setting a mask to the image (pixmap) and drawing a RoundRect on it cause the corners to be circular.
that worked but it made all four corners of the image to be circular.
is there a way to make only the top part as circular?
this is how i made the edges of the pixmap circular:
QBitmap map(100,100); //my pixmap is 100x100
map.fill(Qt::color0);
QPainter painter( &map );
painter.setBrush(Qt::color1);
painter.drawRoundRect(0,0,100,100,20,20);
p.setMask(map);
ui->image1->setPixmap(p);
and this is how i made the QLabel top left and right corner circular
QString style = "border: 4px solid; \n";
style += "border-top-left-radius: 20px;\n";
style += "border-top-right-radius: 20px;";
ui->image1->setStyleSheet(style);
Your idea with the mask is not too bad. You just have to do some composite drawing to the mask, e.g.
QPainter painter(&map);
painter.setBrush(Qt::color1);
painter.drawRoundedRect(0, 0, 100, 40, 20, 20);
painter.drawRect(0, 20, 100, 100);
p.setMask(map);

Pixels at arrow tip missing when using antialiasing

I am trying to draw an arrow with OpenCV 3.2:
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
int main()
{
Mat image(480, 640, CV_8UC3, Scalar(255, 255, 255)); //White background
Point from(320, 240); //Middle
Point to(639, 240); //Right border
arrowedLine(image, from, to, Vec3b(0, 0, 0), 1, LINE_AA, 0, 0.1);
imshow("Arrow", image);
waitKey(0);
return 0;
}
An arrow is drawn, but at the tip some pixels are missing:
To be more precise, two columns of pixels are not colored correctly (zoomed):
If I disable antialiasing, i.e., if I use
arrowedLine(image, from, to, Vec3b(0, 0, 0), 1, LINE_8, 0, 0.1);
instead (note the LINE_8 instead of LINE_AA), the pixels are there, albeit without antialiasing:
I am aware that antialiasing might rely on neighboring pixels, but it seems strange that pixels are not drawn at all at the borders instead of being drawn without antialiasing. Is there a workaround for this issue?
Increasing the X coordinate, e.g. to 640 or 641) makes the problem worse, i.e., more of the arrow head pixels disappear, while the tip still lacks nearly two complete pixel columns.
Extending and cropping the image would solve the neighboring pixels issue, but in my original use case, where the problem appeared, I cannot enlarge my image, i.e., its size must remain constant.
After a quick review, I've found that OpenCV draws AA lines using a Gaussian filter, which contracts the final image.
As I've suggested in comments, you can implement your own function for the AA mode (you can call the original one if AA is disabled) extending the points manually (see code below to have an idea).
Other option may be to increase the line width when using AA.
You may also simulate the AA effect of OpenCV but on the final image (may be slower but helpful if you have many arrows). I'm not an OpenCV expert so I'll write a general scheme:
// Filter radius, the higher the stronger
const int kRadius = 3;
// Image is extended to fit pixels that are not going to be blurred
Mat blurred(480 + kRadius * 2, 640 + kRadius * 2, CV_8UC3, Scalar(255, 255, 255));
// Points moved a according to filter radius (need testing, but the idea is that)
Point from(320, 240 + kRadius);
Point to(639 + kRadius * 2, 240 + kRadius);
// Extended non-AA arrow
arrowedLine(blurred, ..., LINE_8, ...);
// Simulate AA
GaussianBlur(blurred, blurred, Size(kRadius, kRadius), ...);
// Crop image (be careful, it doesn't copy data)
Mat image = blurred(Rect(kRadius, kRadius, 640, 480));
Another option may be to draw the arrow in an image twice as large and the scale it down with a good smoothing filter.
Obviously, last two options will work only if you don't have any previous data on the image. If so, then use a transparent image for temporal drawing and overlay it at the end.

Fill Masked Contour Intersected with Rect

I've got a Mat image which is a binary mask that I segmented and a cv::Rect that identifies an specific region. When I get the contours of the binary mask the image is like this:
Binary Mask
Contours generated
I would like to fill in the mask the region that intersects with the rectangle. How would I do that?
Thanks in advance.
There is way simpler than #ZdaR's solution: using Regions Of Interest (ROI) which directly selects the bounding rectangle region to process.
cv::Rect boundingRect(/* x, y, width, height */);
contours_image(boundingRect).setTo(255, binary_image(boundingRect));
Here, I select each region with operator parenthesis contours_image(boundingRect) and binary_image(boundingRect), and use the binary image part as a mask to set all corresponding pixels to 255.
A good choice would be to use cv::min() with the binary image and another cv::Mat() with the area under cv::Rect() painted as white. It will filter out the desired portion under the Rect as:
// Create a grayscale canvas with black background
cv::Mat canvas = cv::Mat(binary_img.size(), CV_8UC1, cv::Scalar(0));
// I created a dummy rect replace it with original rect coordinates.
cv::Rect boundingRect = cv::Rect(100, 100, 200, 200);
// Draw filled rect onto the black canvas with white color
cv::rectangle(binary_image, boundingRect, cv::Scalar(255), -1);
// Take the min of binary image and canvas to filter out the contours
cv::min(binary_image, canvas, binary_image);
EDIT:
If you want to filter the contours which intersect the cv::Rect, then you need to iterate over each contour, calculate the boundingRect and check if it intersects the given rect.
for (int i=0; i<contours.size(); i++) {
if ((cv::boundingRect(contours[i]) & boundingRect).area() > 0) {
// Your desired contours found.
}
}

How to fill a Rectangle (a Rect object) with an image in Qt?

I've tried this:
QBrush brush(QPixmap(":/new/prefix1/car.jpg"));
painter.setBrush(brush);
QRectF car(positions[i],120, 20, 10 );
painter.drawRect(car);
It shows the image but it repeats itself during a simulation in QPaint.
I want a way to fill a rectangle with an image but i'm not finding any specific methods for Rect. Any tricks for that?
Use QPainters drawPixmap. There is an overloaded function, that takes both a QPixmap and a QRect into which the pixmap will be painted:
http://doc.qt.io/qt-5/qpainter.html#drawPixmap-9

Qt : Drawing Blended Transparent Lines/Curves/Paths

I have been able to draw long transparent curves with the QPainterPath so I wont get overlapping opacity joints that would result in connecting lines between points like in Scribble. But is there a way to make a path blend its continuous transparency through out in Qt as such:
I suspect the most visually satisfying solution will be to render the strokes yourself. For example, the image you posted was rendered by drawing a large number of partially-transparent circles over one another. This could be optimized by rendering a large number of ellipses onto a QImage, then later drawing the pre-rendered image to save time.
With the help of this question/answer I wrote this code that does the job:
/* Start and end point. */
const QPointF start{ 0,0 };
const QPointF end{ 100,100 };
QGraphicsLineItem line{ QLine(start, end) };
/* Make the Gradient for this line. */
QLinearGradient gradient(start, end);
QColor color(123, 123, 231); //some color
color.setAlphaF(0.9); //change alpha
gradient.setColorAt(0, color);
color.setAlphaF(0.1); //change alpha again
gradient.setColorAt(1, color );
/* Set the line's pen. */
QPen pen(QBrush(gradient), 10);
line.setPen(pen);