Why is a generated SVG image less rich than the corresponding PNG image - python-3.x

To set this up, I used svgwrite library to create a sample SVG image (20 squares of length 100 at random locations on a display size of length 400)
import svgwrite
import random
random.seed(42)
dwg = svgwrite.Drawing('x.svg', size=(400,400))
dwg.add(dwg.rect(insert=(0,0), size=('100%', '100%'), fill='white')) # White background
for i in range(20):
coordinates = (random.randint(0,399), random.randint(0,399))
color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
dwg.add(dwg.rect(coordinates, (100, 100),
stroke='black',
fill=svgwrite.rgb(*color),
stroke_width=1)
)
dwg.save()
I then wrote a sample pygame program to generate a PNG image of the same sample. (A seed has been used to generate the same sequence of squares.)
import pygame
import random
random.seed(42)
display = pygame.display.set_mode((400,400))
display.fill((255,255,255)) # White background
for i in range(20):
coordinates = (random.randint(0,399), random.randint(0,399))
color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
pygame.draw.rect(display, color, coordinates+(100,100), 0)
pygame.draw.rect(display, (0,0,0), coordinates+(100,100), 1) #For black border
pygame.image.save(display, "x.png")
These are the images that I got (SVG's can't be uploaded to SO, so I have provided a screenshot. Nevertheless, the programs above can be run to output the same).
My question is, why is the PNG (on the left) richer and sharper than the corresponding SVG image? The SVG looks blurred and bland, comparatively.
EDIT: One can notice the fine white line between the first two squares at the top-left corner. It's not very clear in the SVG.

Two things I think may impact:
You are using an image viewer, which could distort the vectorial SVG image. I think all of the vector images viewers get the actual screen size, then export the vectorial image into a matrix image sized in function of the size of the screen you have. Then they display the matrix image. If they render the image with softened sharpness, or if they have a problem by getting the size of your screen, the image may be blurred.
To make the PNG image, you use pygame. But you are using another module to make the SVG image. This module may function differently, and also exports the image with another quality than if you were exporting it with pygame.
For me personally the SVG image appears blurred with Gimp, for example, but not with another SVG viewer.
So I think the problem comes from your image viewer.

Related

CNN: Why do we first resize the image to 256 and then center crop to 224?

The transformation for Alexnet image input is below:
transforms.Resize(256),
transforms.CenterCrop(224),
Why do we first resize the image to 256 and then center crop to 224? I know that 224x224 is the default image size of ImageNet but why we can't directly resize the image to 224x224?
Perhaps this is best illustrated visually. Consider the following image (128x128px):
Say we would resize it to 16x16px directly, we'd end up with:
But if we'd resize it to 24x24px first,
and then crop it to 16x16px, it would look like this:
As you see, it's getting rid of the border, while retains details in the center. Note the differences side by side:
The same applies to 224px vs 256px, except this is at a larger resolution.

How to programmatically (preferably using PIL in python) calculate the total number of pixels of an object with a stripped background?

I have multiple pictures, each of which has an object with its background removed. The pictures are 500x400 pixels in size.
I am looking for a way to programmatically (preferably using python) calculate the total number of pixels of the image inside the picture (inside the space without the background).
I used the PIL package in Python to get the dimensions of the image object, as follows:
print(image.size)
This command successfully produced the dimensions of the entire picture (500x400 pixels) but not the dimensions of the object of interest inside the picture.
Does anyone know how to calculate the dimensions of an object inside a picture using python? An example of a picture is embedded below.
You could floodfill the background pixels with some colour not present in the image, e.g. magenta, then count the magenta pixels and subtract that number from number of pixels in image (width x height).
Here is an example:
#!/usr/bin/env python3
from PIL import Image, ImageDraw
import numpy as np
# Open the image and ensure RGB
im = Image.open('man.png').convert('RGB')
# Make all background pixels magenta
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=50)
# Save for checking
im.save('floodfilled.png')
# Make into Numpy array
n = np.array(im)
# Mask of magenta background pixels
bgMask =(n[:, :, 0:3] == [255,0,255]).all(2)
count = np.count_nonzero(bgMask)
# Report results
print(f"Background pixels: {count} of {im.width*im.height} total")
Sample Output
Background pixels: 148259 of 199600 total
Not sure how important the enclosed areas between arms and body are to you... if you just replace all greys without using the flood-filling technique, you risk making, say, the shirt magenta and counting that as background.

Recognizing overlapping objects

I am processing an image with OpenCV on Python and I want to count every objects (worms) on it. Worms are rather light beige whereas the background is black (see picture) so it is rather easy to distinguish them. The problem is that sometimes worms are too close to each other (sometimes they even overlap) and cv.findContours() will draw one big contour instead of two smaller ones (see picture below).
Because I am using cv.foundContours(), I have to first turn the picture into black and white, then blur it (optional) and finally threshold it in order to have white worms in a black background.
I am using the following code :
import cv2 as cv
img = cv.imread('worms.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
blur=cv.GaussianBlur(gray,(5,5),1)
ret,osu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
imsource,contours,test = cv.findContours(osu,1,1)
cv.drawContours(img,contours,-1, (0,0,255),2)
I tried to erode the thresholded picture but it doesn't work well since the "bond" between two worms is quite big.
Thanks for the help

How to draw a rectangle with edge roughness

I use Imagedraw module to draw rectangle, which is very simple:
blank=Image.new("RGB",[pixelx,pixely],"black:")
draw=ImageDraw.Draw(blank)
draw.rectangle(x1,y1,x2,y2,fill='white')
This gives me a straight line rectangle in white.
But can I change the roughness of edge of this rectangle?
I am trying to make the rectangle look more similar to practical image.
If I cannot achieve this by Imagedraw, what module can help me do that.
Thanks a lot!
It's difficult to know precisely what effect you are after.
I can tell you that Pillow has filters - https://pillow.readthedocs.io/en/5.2.x/reference/ImageFilter.html
from PIL import ImageFilter
im = im.filter(ImageFilter.BLUR)
Changing the angle of your rectangle - by selecting co-ordinates that are not a flat rectangle - would create pixelated edges.
Otherwise, I might suggest randomly changing individual pixels along the edges using https://pillow.readthedocs.io/en/5.2.x/reference/PixelAccess.html
from PIL import Image
px = im.load()
px[0, 0] = (0, 0, 0)
Although that could be slow. It depends on the size of your image and the need for speed.

How can i force the imagemagick module of nodejs to output one single image only?

I am using the imagemagick module with Nodejs
im = require('imagemagick');
The imagemagick module uses the imagemagick command line tools.
I use the convert method to crop an image
im.convert([image_path, '-crop', '200x150', '-gravity', 'center', target_path],
function(err, stdout){}
);
This results in two images. The one with the cropped image area - the second with the image garbage i tried to get rid of.
How can i force imagemagick to output one image file only?
Per the imagemagick documentation for cropping, which is admittedly a little obtuse (emphasis added):
The width and height of the geometry argument give the size of the image that remains after cropping, and x and y in the offset (if present) gives the location of the top left corner of the cropped image with respect to the original image.
...
If the x and y offsets are present, a single image is generated, consisting of the pixels from the cropping region.
...
If the x and y offsets are omitted, a set of tiles of the specified geometry, covering the entire input image, is generated.
... so, you just need to specify your x and y offsets as part of your geometry argument, like so: 200x150-100-75
Notice that I've specified -100 and -75 for the upper left corner of your crop region, this is because you set your gravity to center, but it appears that imagemagick tries to intelligently determine the appropriate distance target based on your gravity, and I don't see exactly how it behaves when you choose center. So you may have to play around with this one a bit, or you could omit the gravity and use the actual offset from the top left corner of your original image.
I had to use the +delete parameter to remove the last image from the image sequence.
im.convert([image_file.path, '-crop', geometry, '+delete', thumb_path], ...

Resources