I am in the process of making a Fabric.js based application that users can upload images and make posters. Recently I started work on a clip function to crop images using a path drawn by the user.
How it works: User selects an image from the canvas and clicks on a crop button. The activeElement is choose from the canvas, its image src is extracted. This SRC is used to place an image on a canvas on a Bootstra Modal. The user clicks on the image where a path will be drawn and finally crops the image.
Once the crop button is clicked, a SVG path is made from the dot points. Then this value is used to crop the image.
Issue: If the image is a PNG with no background ( we only use PNGs ), it will make the background 'black'.
Below, #_path refers to the fabric. Path object created using a svg path by user clicks. #_image is image fabric.Image.
clipPath: () ->
path_x = #_path.getLeft() - ( #_image.getWidth() / 2 )
path_y = #_path.getTop()- ( #_image.getHeight() / 2 )
#_path.setLeft path_x
#_path.setTop path_y
# console.log 'Fill white'
# #_image.setFill('white')
#_image.clipTo = ( ctx ) =>
#_path.render( ctx )
#_image._clipToPath = #getPathSVG()
#_canvas.renderAll()
return
I tried #_image.setFIll('white') and #_image.backgroundColor = 'rgba(255,255,255,1)' but doesn't seem to be having an effect.
Questions:
Have I clipped/cropped the image the correct way ( ie: using clipTo )? Why do you think
Why do you think the image is black where there was trnsparency?
Thanks in advance.
Related
I have a PDF file with Multiple choise questions . The answered are marked . I would like to remove the "tick"portion of it without majorly altering the sheet . I would like to automate it for the entire PDF .In the images below , would like to remove the "tick" mark alone . My input file is a PDF . For sake of representation have give a image .
Note . I use GNU/Linux
[
The main difference that I can see between the checkmark and the rest of the image is the thickness of the black line. Thus we can replace the thicker black line with a sample of the background of your image
Here is a way to do this using Python/OpenCV/Numpy:
import cv2
import numpy as np
#load the image
img = cv2.imread("pen_mark.png")
#Threshold the image to a 2-channel black and white image with the text as white and the background as black.
black_and_white = (img < 128).astype(np.uint8) * 255
#erode the thin lines, leaving only the thicker pen-drawn checkmark.
checkmark = cv2.morphologyEx(black_and_white, cv2.MORPH_ERODE, np.ones((5,5)))
#dilate the remaining checkmark back to slightly larger than its original size
checkmark = cv2.morphologyEx(checkmark, cv2.MORPH_DILATE, np.ones((7,7)))
#switch the left and right side of the original image in order to get a sample of the
#original speckled background overlaying the position of the checkmark.
rolled_img = np.roll(img, shift=img.shape[1]//2)
#replace the pixels of the original checkmark with the background
img[checkmark.nonzero()] = rolled_img[checkmark.nonzero()]
#save the new image
cv2.imwrite("result.png", img)
Note that this probably won't be a reliable solution for all of your PDFs. I assume the thickness of the pen mark will change. There will probably also be cases where the pen mark overlaps other lines and you'll end up with blank spots. Another problem is the difficulty in setting the background so that it blends nicely with its surroundings.
I scanned a couple pages and edited them a little so the look neat.
But as I wanted to reprint them I noticed that I cropped the border from these images so that the printer won't print the whole content.
I have a Brother printer which has Linux drivers but somehow the scaling option does not work.
So I thought to scale the PDF (in which I have converted these scans) but the printer driver scales them back to fit the page. (If I disable the scale-to-fit option it becomes garbage)
So I thought that I could do some script-fu to resize these images with a couple of lines and add some padding for the printer. But I have no clue how to do this.
Here's my first attempt:
(define (resize-image filename-in filename-out )
(let* ((image (car (gimp-file-load RUN-NONINTERACTIVE filename-in "")))
(drawable (car (gimp-image-active-drawable image)))
)
(gimp-image-resize image 2728 3819 124 173)
(gimp-file-save RUN-NONINTERACTIVE image drawable filename-out "")
)
)
This does not work. The image simply remains unchanged.
My page is A4 with 2480x3472, so I thought to add 10% to the width so it becomes 2728x3819 and set the offset to 5% so the content is centered (with offset values 124 and 173).
Wnen you resize the image you are just extending the canvas but not the layer. And gimp-file-save only saves the active "drawable" (layer in your case), so you just save the same image. What you have to do is either:
add a white layer at the bottom of the layer stack
flatten the image
save the result
or
remove the layer's alpha channel (assuming the background paint color is white)
extend the layer to cover the canvas (the extension will be filled with white): gimp-layer-resize-to-image-size
save the result
I am using PdfStamper getOverContent() so I can add an image to the output PDF file using an AffineTransform of Identity type.
PdfContentByte content = stamper.getOverContent(1);
data.image.setAbsolutePosition(desc.X,desc.Y);
content.addImage(data.image,desc.transform);
//content.addImage(data.image);
if I use the commented line without the transform it works perfectly adding the image to the PDF generated but with the AffineTransform (setToIdentity()) it does not show up.
could someone help me out with that ? I intend to use more sophisticated transform but the Identity should work first...
EDIT (copied from the invalid answer)
I removed the call to setAbsolutePosition and used a setToIdentity() as the only transformation and the image is not show... Then added the setToTranslation(X,Y) where X and Y are the same values used in the successful case where I do NOT give the transformation as the second parameter and still it does NOT show the image. Is there an example with the AffineTransform as a parameter to PdfContentByte addImage() call using an AffineTransform as a parameter ? I have bought your book but could not fins any.
I have examined your problem and I am pretty sure that your image gets added. However: you can't see it because the dimension of the image is 1 user unit by 1 user unit.
I've made an example to show you how to fix this problem: AddImageAffineTransform
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Image image = Image.getInstance(IMG);
AffineTransform at = AffineTransform.getTranslateInstance(36, 300);
at.concatenate(AffineTransform.getScaleInstance(image.getScaledWidth(), image.getScaledHeight()));
PdfContentByte canvas = stamper.getOverContent(1);
canvas.addImage(image, at);
stamper.close();
reader.close();
}
In this example, I start with a translation: 36 user units from the left border and 300 user units from the bottom. If I would add the image using this transformation, I'd add the image at those coordinates, but it would be too small to see with the naked eye.
To make sure the image is visible, I concatenate a scale transformation scaling the image to its width in X direction and to its height in Y direction.
I imagine this is a shot in the dark, but is it possible to have a vector file of a shape (in this case a hexagon with rounded corners), and pass an image through some code and have it coming out cropped in the shape of that vector?
I'm attempting to utilize hexagons in my design and have gone through every page I possibly can. I've seen the many HTML and CSS solutions, but none of them achieve what I'm looking for flawlessly.
Another idea I have is maybe overlaying a transparent hexagon shape with white corners on top of the existing image with imagemagick, and then going through and making any white transparent. Thoughts?
I don't have any code for cropping in the shape of a vector file, but here's what I have for overlaying an outline of the shape I want on top of the other picture:
imgfile = "public/" + SecureRandom.uuid + ".png"
SmartCropper.from_file(art.url(:original)).smart_square.resize(225,225).write(imgfile)
overlay = Magick::Image.read("app/assets/images/overlay.png")
img = Magick::Image.read(imgfile)
img.composite(overlay,0,0, Magick::OverCompositeOp)
Right now it's giving me an undefined method error for composite, which is strange because I've followed some other stack overflow questions using the same thing in their models.
Any help is appreciated!
You have fallen for a common ImageMagick trap - the objects you get from the .read method are not Magick::Image objects but Magick::ImageList ones, and for most image types you want the first item from the list.
Without knowing how you have set up your overlay.png file, it is difficult to tell what the best composite option is. However, in a similar situation I found CopyOpacityCompositeOp to be useful, and to have the overlay's transparency control the transparency in the final image.
I tested the following code and it looks like it would do what you want if overlay.png was set up that way:
require 'smartcropper'
imgfile = "test_square.png"
SmartCropper.from_file( 'test_input.png' ).
smart_square.resize( 225, 225 ).write( imgfile )
overlay = Magick::Image.read( 'overlay.png' ).first
img = Magick::Image.read( imgfile ).first
img.composite( overlay, 0, 0, Magick::CopyOpacityCompositeOp ).
write( "test_result.png" )
Instead of reading overlay from a file, you could create it using Magick::Draw like this:
overlay = Magick::Image.new( 225, 225 ) do |i|
i.background_color= "Transparent"
end
gc = Magick::Draw.new
gc.stroke('white').stroke_width(10)
gc.fill('white')
gc.polygon(97.5, 26.25, 178.5, 73.125, 178.5, 167,
97.5, 213.75, 16.5, 167, 16.5, 73.125)
gc.draw( overlay )
NB That's a hexagon, but I've not bothered centering it.
I would like to replace a part of the image with my image in Opencv
I used
cvGetPerspectiveMatrix() with a warpmatrix and using cvAnd() and cvOr()
but could not get it to work
This is the code that is currently displaying the image and a white polygon for the replacement image. I would like to replace the white polygon for a pic with any dimension to be scaled and replaced with the region pointed.
While the code is in javacv I could convert it to java even if c code is posted
grabber.start();
while(isDisp() && (image=grabber.grab())!=null){
if (dst_corners != null) {// corners of the image to be replaced
CvPoint points = new CvPoint((byte) 0,dst_corners,0,dst_corners.length);
cvFillConvexPoly(image,points, 4, CvScalar.WHITE, 1, 0);//white polygon covering the replacement image
}
correspondFrame.showImage(image);
}
Any pointers to this will be very helpful.
Update:
I used warpmatrix with this code and I get a black spot for the overlay image
cvSetImageROI(image, cvRect(x1,y1, overlay.width(), overlay.height()));
CvPoint2D32f p = new CvPoint2D32f(4);
CvPoint2D32f q = new CvPoint2D32f(4);
q.position(0).x(0);
q.position(0).y(0);
q.position(1).x((float) overlay.width());
q.position(1).y(0);
q.position(2).x((float) overlay.width());
q.position(2).y((float) overlay.height());
q.position(3).x(0);
q.position(3).y((float) overlay.height());
p.position(0).x((int)Math.round(dst_corners[0]);
p.position(0).y((int)Math.round(dst_corners[1]));
p.position(1).x((int)Math.round(dst_corners[2]));
p.position(1).y((int)Math.round(dst_corners[3]));
p.position(3).x((int)Math.round(dst_corners[4]));
p.position(3).y((int)Math.round(dst_corners[5]));
p.position(2).x((int)Math.round(dst_corners[6]));
p.position(2).y((int)Math.round(dst_corners[7]));
cvGetPerspectiveTransform(q, p, warp_matrix);
cvWarpPerspective(overlay, image, warp_matrix);
I get a black spot for the overlay image and even though the original image is a polygon with 4 vertices the overlay image is set as a rectangle. I believe this is because of the ROI. Could anyone please tell me how to fit the image as is and also why I am getting a black spot instead of the overlay image.
I think cvWarpPerspective(link) is what you are looking for.
So instead of doing
CvPoint points = new CvPoint((byte) 0,dst_corners,0,dst_corners.length);
cvFillConvexPoly(image,points, 4, CvScalar.WHITE, 1, 0);//white polygon covering the replacement image
Try
cvWarpPerspective(yourimage, image, M, image.size(), INTER_CUBIC, BORDER_TRANSPARENT);
Where M is the matrix you get from cvGetPerspectiveMatrix
One way to do it is to scale the pic to the white polygon size and then copy it to the grabbed image setting its Region of Interest (here is a link explaining the ROI).
Your code should look like this:
resize(pic, resizedImage, resizedImage.size(), 0, 0, interpolation); //resizedImage should have the points size
cvSetImageROI(image, cvRect(the points coordinates));
cvCopy(resizedImage,image);
cvResetImageROI(image);
I hope that helps.
Best regards,
Daniel