Center alignment of text in PyX - python-3.x

I am using PyX to create rectangles and putting some (LaTex) text within the rectangle.
# python 3
from pyx import *
text.set(text.LatexRunner)
c = canvas.canvas()
c.stroke(path.rect(0, 0, 1, 1), [style.linewidth.Thick,
color.rgb.red,
deco.filled([color.rgb.green])])
c.text(0.25, 0.25, r"\LaTeX{}")
c.writePDFfile("text-centered") # creates a PDF file
The above code snippet creates a PDF:
As you can see, the text is not aligned. It is possible to try manually until it is (visibly) centered, but this is problematic for obvious reasons.
My question: Is it possible to align the text centered (both horizontally and vertically) automatically?

just use
c.text(0.5, 0.5, r"\LaTeX{}", [text.halign.center, text.vshift.mathaxis])
see
http://pyx.sourceforge.net/manual/text.html#text.halign.center
http://pyx.sourceforge.net/manual/text.html#text.vshift.mathaxis
http://pyx.sourceforge.net/examples/text/halign.html
http://pyx.sourceforge.net/examples/text/valign.html
for some documentation and examples

Related

Checkerboard pattern in saved image

I am trying to save a png image using the following commands:
fig = plt.figure(figsize=(14, 8))
ax1 = fig.add_subplot(221)
subplt1=(usub1_sfc-usub2_sfc).plot(vmin=-2.5e-2,vmax=2.5e-2,add_colorbar=False)
cb=plt.colorbar(subplt1,extend='both')
cb.ax.set_title('m/s', size=14)
cb.ax.tick_params(labelsize=12)
ax1.tick_params(labelsize=12)
ax1.set_xticks(np.arange(0,3500,500))
ax1.set_yticks(np.arange(0,2500,500))
#plt.xticks(fontsize=10)
#fig.colorbar(subplt1)
plt.title('USUBM$_{\mathrm{1km}}$ - USUBM$_{\mathrm{5km}}$')
plt.xlabel('nlon',fontsize=16)
plt.ylabel('nlat',fontsize=16)
ax2 = fig.add_subplot(222)
subplt2=(usub3_sfc-usub2_sfc).plot(vmin=-2.5e-2,vmax=2.5e-2,add_colorbar=False)
cb=plt.colorbar(subplt2,extend='both')
cb.ax.set_title(label='m/s', size=14)
cb.ax.tick_params(labelsize=12)
ax2.tick_params(labelsize=12)
ax2.set_xticks(np.arange(0,3500,500))
ax2.set_yticks(np.arange(0,2500,500))
plt.title('USUBM$_{\mathrm{200m}}$ - USUBM$_{\mathrm{5km}}$')
plt.xlabel('nlon',fontsize=16)
plt.ylabel('nlat',fontsize=16)
fig.savefig('./test.png',dpi=130)
My png file ends up having a checkerboard pattern everywhere around the bounding boxes of the plots. Inside the boxes I can see the fields, but everywhere around it the checkerboard pattern covers the axis ticks, axis labels, plot titles, etc.
The file I create looks very much like the third image at this link. The only difference is that there you see the checkerboard everywhere.
Question: How to save the png image without this checkerboard pattern?
Here is the answer to my original question (based on the other thread I linked to):
fig = plt.figure(facecolor="w")
This removed the checkerboard pattern surrounding the plotted area.

Transform Plates into Horizontal Using Hough transform

I am trying to transform images that are not horizontal, because they may be slanted.
It turns out that when testing 2 images, this photo that is horizontal, and this one that is not. It gives me good results with the horizontal photo, however when trying to change the second photo that is tilted, it does not do what was expected.
The fist image it's works fine like below with a theta 1.6406095. For now it looks bad because I'm trying to make the 2 photos look horizontally correct.
The second image say that theta is just 1.9198622
I think the error it is at this line:
lines= cv2.HoughLines(edges, 1, np.pi/90.0, 60, np.array([]))
I have done a little simulation on this link with colab.
Any help is welcome.
So far this is what I got.
import cv2
import numpy as np
img=cv2.imread('test.jpg',1)
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgBlur=cv2.GaussianBlur(imgGray,(5,5),0)
imgCanny=cv2.Canny(imgBlur,90,200)
contours,hierarchy =cv2.findContours(imgCanny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
rectCon=[]
for cont in contours:
area=cv2.contourArea(cont)
if area >100:
#print(area) #prints all the area of the contours
peri=cv2.arcLength(cont,True)
approx=cv2.approxPolyDP(cont,0.01*peri,True)
#print(len(approx)) #prints the how many corner points does the contours have
if len(approx)==4:
rectCon.append(cont)
#print(len(rectCon))
rectCon=sorted(rectCon,key=cv2.contourArea,reverse=True) # Sort out the contours based on largest area to smallest
bigPeri=cv2.arcLength(rectCon[0],True)
cornerPoints=cv2.approxPolyDP(rectCon[0],0.01*peri,True)
# Reorder bigCornerPoints so I can prepare it for warp transform (bird eyes view)
cornerPoints=cornerPoints.reshape((4,2))
mynewpoints=np.zeros((4,1,2),np.int32)
add=cornerPoints.sum(1)
mynewpoints[0]=cornerPoints[np.argmin(add)]
mynewpoints[3]=cornerPoints[np.argmax(add)]
diff=np.diff(cornerPoints,axis=1)
mynewpoints[1]=cornerPoints[np.argmin(diff)]
mynewpoints[2]=cornerPoints[np.argmax(diff)]
# Draw my corner points
#cv2.drawContours(img,mynewpoints,-1,(0,0,255),10)
##cv2.imshow('Corner Points in Red',img)
##print(mynewpoints)
# Bird Eye view of your region of interest
pt1=np.float32(mynewpoints) #What are your corner points
pt2=np.float32([[0,0],[300,0],[0,200],[300,200]])
matrix=cv2.getPerspectiveTransform(pt1,pt2)
imgWarpPers=cv2.warpPerspective(img,matrix,(300,200))
cv2.imshow('Result',imgWarpPers)
Now you just have to fix the tilt (opencv has skew) and then use some threshold to detect the letters and then recognise each letter.
As for a general purpose, I think images need to be normalised first so that we can easily detect the edges.

Why does this code slow down? Graphics.py?

I have some code that reads a small BMP (128x96) file and puts the RGB values into a list.
I then run a nested loop and read the RGB values in reverse from the list and draw them on the screen.
It starts quite quickly and draws the first 20 lines in a second, but progressively slows down to such an extent I've never seen it finish. It only a small 128x96 image.
I feel it's the calls to the graphics.py library, buy why, or is it something else?
I'm running this on a raspberry pi, if that's of use. Python 3.4.2
If your interested in trying you can find the supporting files here https://drive.google.com/open?id=1yM9Vn1Nugnu79l1UNShamEAGd2VWF3T4
(It's the graphics.py library I'm using and the tiny bmp file, also the actual file in question called SlowDownWhy.py)
import math
import sys
from graphics import *
from PIL import Image
# Initialise Vars for Image width n height
iw=0
ih=0
img=Image.open("ani1.bmp","r") # Open Image
iw, ih = img.size # Set image width n height
ch = int(1000/ih) # Cube height set
cw = ch # Cube width set
win = GraphWin("My Window", iw*cw, ih*ch)
win.setBackground(color_rgb(128,128,128))
#Transfer Bitmap RGB vales to csv list - 'RGBlist'
pix_val = list(img.getdata())
RGBlist = [x for sets in pix_val for x in sets]
noe = (iw * ih * 3)-3
x = iw
y = ih
for vy in list(range(ih)):
y = y-1
x = iw
for vx in list(range(iw)):
x = x-1
r=RGBlist[noe]
g=RGBlist[noe+1]
b=RGBlist[noe+2]
noe=noe-3
cx=x*cw
cy=y*ch
aPoint = Rectangle(Point(cx,cy), Point(cx+cw,cy+ch))
aPoint.setFill(color_rgb(r,g,b))
aPoint.draw(win)
It should create a window no bigger than 1000 pixels in height and start drawing the picture from the bottom right to the top left, line by line. but slows down progressively.
Ignoring the invalid syntax, this is simply because of the way graphics.py is programmed: It is not designed to handle this many objects put onto the screen. (It uses tkinter in the back-end, which will slow down with 128*96=12,288 objects). For rendering images, you should either directly integrate them or use another library, as example pygame.
To integrate it into the graphics.py program, there is the Image-class, which you overwrote with the PIL.Image-library (this is the reason why you never do import *). Look here: Importing custom images into graphics.py

How can I quickly change pixels in a image from a color dictionary?

I have an image, I want to change all the colors in the image from a color map eg. {(10,20,212) : (60,40,112)...}
Currently, I am reading the image OpenCV and then iterating over the image array and changing each pixel, but this is very slow.
Is there any way I can do it faster?
I am providing two answers to this question. This answer is more based in OpenCV and the other is more based in PIL/Pillow. Read this answer in conjunction with my other answer and potentially mix and match.
You can use Numpy's linalg.norm() to find the distances between colours and then argmin() to choose the nearest. You can then use a LUT "Look Up Table" to look up a new value based on the existing values in an image.
#!/usr/bin/env python3
import numpy as np
import cv2
def QuantizeToGivenPalette(im, palette):
"""Quantize image to a given palette.
The input image is expected to be a Numpy array.
The palette is expected to be a list of R,G,B values."""
# Calculate the distance to each palette entry from each pixel
distance = np.linalg.norm(im[:,:,None] - palette[None,None,:], axis=3)
# Now choose whichever one of the palette colours is nearest for each pixel
palettised = np.argmin(distance, axis=2).astype(np.uint8)
return palettised
# Open input image and palettise to "inPalette" so each pixel is replaced by palette index
# ... so all black pixels become 0, all red pixels become 1, all green pixels become 2...
im=cv2.imread("image.png",cv2.IMREAD_COLOR)
inPalette = np.array([
[0,0,0], # black
[0,0,255], # red
[0,255,0], # green
[255,0,0], # blue
[255,255,255]], # white
)
r = QuantizeToGivenPalette(im,inPalette)
# Now make LUT (Look Up Table) with the 5 new colours
LUT = np.zeros((5,3),dtype=np.uint8)
LUT[0]=[255,255,255] # white
LUT[1]=[255,255,0] # cyan
LUT[2]=[255,0,255] # magenta
LUT[3]=[0,255,255] # yellow
LUT[4]=[0,0,0] # black
# Look up each pixel in the LUT
result = LUT[r]
# Save result
cv2.imwrite('result.png', result)
Input Image
Output Image
Keywords: Python, PIL, Pillow, image, image processing, quantise, quantize, specific palette, given palette, specified palette, known palette, remap, re-map, colormap, map, LUT, linalg.norm.
I am providing two answers to this question. This answer is more based in PIL/Pillow and the other is more based in OpenCV. Read this answer in conjunction with my other answer and potentially mix and match.
You can do it using the palette. In case you are unfamiliar with palettised images, rather than having an RGB value at each pixel location, you have a simple 8-bit index into a palette of up to 256 colours.
So, what we can do, is load your image as a PIL Image, and quantise it to the set of input colours you have. Then each pixel will have the index of the colour in your map. Then just replace the palette with the colours you want to map to.
#!/usr/bin/env python3
import numpy as np
from PIL import Image
def QuantizeToGivenPalette(im, palette):
"""Quantize image to a given palette.
The input image is expected to be a PIL Image.
The palette is expected to be a list of no more than 256 R,G,B values."""
e = len(palette)
assert e>0, "Palette unexpectedly short"
assert e<=768, "Palette unexpectedly long"
assert e%3==0, "Palette not multiple of 3, so not RGB"
# Make tiny, 1x1 new palette image
p = Image.new("P", (1,1))
# Zero-pad the palette to 256 RGB colours, i.e. 768 values and apply to image
palette += (768-e)*[0]
p.putpalette(palette)
# Now quantize input image to the same palette as our little image
return im.convert("RGB").quantize(palette=p)
# Open input image and palettise to "inPalette" so each pixel is replaced by palette index
# ... so all black pixels become 0, all red pixels become 1, all green pixels become 2...
im = Image.open('image.png').convert('RGB')
inPalette = [
0,0,0, # black
255,0,0, # red
0,255,0, # green
0,0,255, # blue
255,255,255 # white
]
r = QuantizeToGivenPalette(im,inPalette)
# Now simply replace the palette leaving the indices unchanged
newPalette = [
255,255,255, # white
0,255,255, # cyan
255,0,255, # magenta
255,255,0, # yellow
0,0,0 # black
]
# Zero-pad the palette to 256 RGB colours, i.e. 768 values
newPalette += (768-len(newPalette))*[0]
# And finally replace the palette with the new one
r.putpalette(newPalette)
# Save result
r.save('result.png')
Input Image
Output Image
So, to do specifically what you asked with a dictionary that maps old colour values to new ones, you will want to initialise oldPalette to the keys of your dictionary and newPalette to the values of your dictionary.
Keywords: Python, PIL, Pillow, image, image processing, quantise, quantize, specific palette, given palette, specified palette, known palette, remap, re-map, colormap, map.
There are some hopefully useful words about palettised images here, and here.
I think you might find using the built in LUT function of opencv helpful, as documented here.
There is already a python binding for the function, and it takes as input the original matrix and a LUT, and returns the new matrix as an output.
There isn't a tutorial for using it in python, but there is one for using it in C++ which I imagine will be useful, found here. That tutorial lists this method as the fastest one for this sort of problem.

Captions for matshow()s in multiple-page pdf

I have been around this problem for quite a long time but I'm not able to find an answer.
So, I have a list with matrices which I want to plot (for the sake of this question I'm just having 2 random matrices:
list = [np.random.random((500, 500)), np.random.random((500, 500))]
I then want to plot each element of the list using matshow in a separate page of a pdf file:
with PdfPages('file.pdf') as pdf:
plt.rc('figure', figsize=(3,3), dpi=40)
for elem in list:
plt.matshow(elem, fignum=1)
plt.title("title")
plt.colorbar()
plt.text(0,640,"Caption")
pdf.savefig() # saves the current figure into a pdf page
plt.close()
The result is the following:
My problem is with the caption. You can see I put "Caption" in the edge of the document on purpose. This is because sometimes the actual captions I want to insert are too big to fit in one single pdf page.
So, how can I make each pdf page adjustable to the caption's content (that might vary in each page)? For example, would it be possible to set each page size to A4 or A3, and then plot/write everything in each page?
I've already tried setting up plt.figure(figsize=(X, X)) with a variable X size, but it just changes the resolution of the pdf I guess.
You may want to use the bbox_inches="tight" option when saving the file. This will adapt the figure size to its content. So it then suffices to place some text at position (0,0) in figure coordinates and align it to the top. This will then extent towards the bottom and outside the figure (so the figure when shown on screen would not contain that text), but with the bbox_inches="tight" option of savefig, the saved figure will become large enough to contain that text.
The use of the textwrap package will then also allow to limit the text in horizontal direction.
import numpy as np; np.random.seed(1)
import textwrap
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
p = np.ones(12); p[0] = 7
text2 = "".join(np.random.choice(list(" abcdefghijk"),p=p/p.sum(), size=1000))
text2 = textwrap.fill(text2, width=80)
texts = ["Caption: Example", "Caption 2: " + text2 ]
lis = [np.random.random((500, 500)), np.random.random((500, 500))]
with PdfPages('file.pdf') as pdf:
for elem,text in zip(lis,texts):
fig = plt.figure(dpi=100)
grid_size = (3,1)
plt.imshow(elem)
plt.title("title")
plt.colorbar()
fig.text(0,0, text, va="top")
plt.tight_layout()
pdf.savefig(bbox_inches="tight")
plt.close()
I think I have come up with an answer to this question myself, which solves the problem of having enough space for my text:
However, a perfect answer would be making each page's size dynamic, according to the amount of caption I put.
Anyway, my answer is the following (I essentially divided each page in a grid with 3 rows, making the upper 2 rows for the plots, and the last just for the caption) :
with PdfPages('file.pdf') as pdf:
for elem in list:
fig = plt.figure(figsize=(8.27, 11.69), dpi=100)
grid_size = (3,1)
plt.subplot2grid(grid_size, (0, 0), rowspan=2, colspan=1)
plt.imshow(elem)
plt.title("title")
plt.colorbar()
plt.subplot2grid(grid_size, (2, 0), rowspan=2, colspan=1)
plt.axis('off')
plt.text(0,1,"Caption")
plt.tight_layout()
pdf.savefig()
plt.close()
Which produces the following in each page:
Could someone find a better solution? :)

Resources