Converting pixel grid to contours? - python

I have a pixel grid, which can be any kind of 2D array (numpy array for instance) with each value representing a color (max 5 different colors). I'm looking to display it in an OpenGL context, but drawing pixel by pixel is both inefficient and stupid. What I'd like to do is to regroup all adjacent pixel of the same color into one shape, so I can draw those shapes.
Basically, I want to be able to do this:
Going from a 2D array of points to a list of shapes (a shape being a list of vertices).
I have no idea how to proceed, so I'm looking for anything that can do the job, like any algorithm in any language or any Python Library that can do that.

I know you're asking for a vector solution, but I believe that the simple and obvious approach could work just fine for you, and will likely perform better.
I would load your color data into a texture, using a call sequence like (sorry about the C notation, but you should be able to translate this to python bindings easily):
GLuint texId = 0;
glGenTexture(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, colorData);
where width and height are the number of squares in horizontal and vertical direction, and colorData an array with the color for each square in RGB format.
Then it's important to choose the right sampling parameters. Since sharp edges between texels are desirable here, we want "nearest" sampling, instead of the "linear" that is more commonly used for image type textures. This will result in a sharp transition between the squares, instead of interpolating the colors between them:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Then, to render your grid, you render one single textured quad using this texture.
The only advantage I can think of that a vector solution would have over this is if you want to use multisampled anti-aliasing (aka MSAA). Since MSAA only does anti-aliasing on primitive edges, it would not help for the "edges" where you have color transitions between two squares. With a vector based solution, where you render each region as a separate primitive, you would get anti-aliasing for those edges.
As long as you just scale the image during display, the aliasing should not be a problem, though. That would only really come into play if you also wanted to rotate it.

I suggest you to use np.extract function in numpy i think it can be helpful ! this is an example of it :
import numpy as np
arr = np.arange(12).reshape((3, 4))
condition = np.mod(arr, 3)==0 # base on this condition the function extract the special arrays
print np.extract(condition, arr)
and this is result :
[0 3 6 9]
then you can concatenate this array with numpy.concatenate((a1, a2, ...), axis=0) function ! read more at : http://docs.scipy.org/doc/numpy/reference/index.html

Related

How would I make a 2D colormap where each point represents a 3d angle?

I have a HxWx3 array where the 3 denotes x,y,z co-ordinates (so it's HxW 3d points, organised in a 2D array of height H and width W). Actually I only care about the angle that x, y, z makes to the origin. I want to turn this into an RGB image of HxW that's informative. Informative means that angles close to one another should have always have similar colors, and angles far from one another should have different colors.
For context to anyone who works with computer vision: I want to do a colormap of the normal obtained from a depth map.
EDIT - If it makes it any easier I only need one hemisphere, not a whole sphere. So circular consistency only needs to happen in one dimension, not two (I think).

How to get border pixels of an image in python?

I have an image, using steganography I want to save the data in border pixels only.
In other words, I want to save data only in the least significant bits(LSB) of border pixels of an image.
Is there any way to get border pixels to store data( max 15 characters text) in the border pixels?
Plz, help me out...
OBTAINING BORDER PIXELS:
Masking operations are one of many ways to obtain the border pixels of an image. The code would be as follows:
a= cv2.imread('cal1.jpg')
bw = 20 //width of border required
mask = np.ones(a.shape[:2], dtype = "uint8")
cv2.rectangle(mask, (bw,bw),(a.shape[1]-bw,a.shape[0]-bw), 0, -1)
output = cv2.bitwise_and(a, a, mask = mask)
cv2.imshow('out', output)
cv2.waitKey(5000)
After I get an array of ones with the same dimension as the input image, I use cv2.rectangle function to draw a rectangle of zeros. The first argument is the image you want to draw on, second argument is start (x,y) point and the third argument is the end (x,y) point. Fourth argument is the color and '-1' represents the thickness of rectangle drawn (-1 fills the rectangle). You can find the documentation for the function here.
Now that we have our mask, you can use 'cv2.bitwise_and' (documentation) function to perform AND operation on the pixels. Basically what happens is, the pixels that are AND with '1' pixels in the mask, retain their pixel values. Pixels that are AND with '0' pixels in the mask are made 0. This way you will have the output as follows:
.
The input image was :
You have the border pixels now!
Using LSB planes to store your info is not a good idea. It makes sense when you think about it. A simple lossy compression would affect most of your hidden data. Saving your image as JPEG would result in loss of info or severe affected info. If you want to still try LSB, look into bit-plane slicing. Through bit-plane slicing, you basically obtain bit planes (from MSB to LSB) of the image. (image from researchgate.net)
I have done it in Matlab and not quite sure about doing it in python. In Matlab,
the function, 'bitget(image, 1)', returns the LSB of the image. I found a question on bit-plane slicing using python here. Though unanswered, you might want to look into the posted code.
To access border pixel and enter data into it.
A shape of an image is accessed by t= img.shape. It returns a tuple of the number of rows, columns, and channels.A component is RGB which 1,2,3 respectively.int(r[0]) is variable in which a value is stored.
import cv2
img = cv2.imread('xyz.png')
t = img.shape
print(t)
component = 2
img.itemset((0,0,component),int(r[0]))
img.itemset((0,t[1]-1,component),int(r[1]))
img.itemset((t[0]-1,0,component),int(r[2]))
img.itemset((t[0]-1,t[1]-1,component),int(r[3]))
print(img.item(0,0,component))
print(img.item(0,t[1]-1,component))
print(img.item(t[0]-1,0,component))
print(img.item(t[0]-1,t[1]-1,component))
cv2.imwrite('output.png',img)

How to resize an image faster in python

I have an image i.e an array of pixel values, lets say 5000x5000 (this is the typical size). Now I want to expand it by 2 times to 10kx10k. The value of (0,0) pixel value goes to (0,0), (0,1), (1,0), (1,1) in the expanded image.
After that I am rotating the expanded image using scipy.interpolate.rotate (I believe there is no faster way than this given the size of my array)
Next I have to again resize this 10kx10k array to original size i.e. 5kx5k. To do this I have to take the average pixel values of (0,0), (0,1), (1,0), (1,1) in the expanded image and put them in (0,0) of the new image.
However it turns out that this whole thing is an expensive procedure an takes a lot of time given the size of my array. Is there a faster way to do it?
I am using the following code to expand the original image
#Assume the original image is already given
largeImg=np.zeros((10000,10000), dtype=np.float32)
for j in range(5000):
for k in range(5000):
pixel_value=original_img[j][k]
for x in range((2*k), (2*(k+1))):
for y in range((2*j), (2*(j+1))):
largeImg[y][x] = pixel_value
A similar method is used to reduce the image to original size after rotation.
In numpy you can use repeat:
large_img = original_img.repeat(2, axis=1).repeat(2, axis=0)
and
final_img = 0.25 * rotated_img.reshape(5000,2,5000,2).sum(axis=(3,1))
or use scipy.ndimage.zoom. this can give you smoother results than the numpy methods.
there is a nice library that probably has all the functions you need for handling images, including rotate:
http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage.transform.rotate

Using python, how can I display a matrix of RGB values in a window?

I have calculated a matrix of RGB triples for an image and I would like to know the most straightforward technique to display them in an interactive window. I suspect that pygame will be involved. Techniques that minimize the use of pip will be given preference.
result = numpy.zeros([height,width, 3], dtype=numpy.uint8)
pyopencl.enqueue_copy(queue, result, result_g).wait()
surface = pygame.display.set_mode((width, height), pygame.DOUBLEBUF)
# now what?
The solution I was able to get working was this:
result = numpy.zeros([height,width, 3], dtype=numpy.uint8)
pyopencl.enqueue_copy(queue, result, result_g).wait()
surface = pygame.display.set_mode((width, height), pygame.DOUBLEBUF)
rgb2 = numpy.transpose(rgb, (1,0,2))
pygame.pixelcopy.array_to_surface(surface, rgb2)
pygame.display.flip()
The transpose is only necessary because my opencl kernel computed an image arranged as result[y,x,:] = (r,g,b) whereas array_to_surface expects result[x,y,:] (which is backwards from how most framebuffers work). I could alter my opencl kernel to store things in column-major order if I wanted to avoid the transpose.
This solution only works because my surface is the exact same dimensions as my image. I will upvote any other good solutions that work when the surface and the pixel matrix are different dimensions (because someone might find this article when searching for that).
It is difficult to answer exactly without knowing what the code you have shown does, but something like this:
for c in range(width):
for d in range(height):
color = result(d,c)
pygame.draw.line(surface, color, (c,d),(c+1,d))

Transform a PyGame black and white display to a 7x5 bit Matrix

I have a Pygame black display on which I will draw a letter with white color, as shown in the image below. The size of the display can be anything above 100x100 pixels.
I know I can use something like this to get the surface 2d array:
miSuface = pygame.display.get_surface()
miCoso = pygame.surfarray.array2d(miSuface)
However, I would like to somehow translate this array to a 7x5 bit matrix, on which 0 will correspond to a black pixel and 1 to a white pixel. My final intent is to use the matrix to train a neural network and create a simple OCR. Is there any way I can achieve this? Or is there a better approach to get the 7x5 matrix?
I don't know of any way offhand to compress your array2d into either a smaller array or one with 1-bit of color information. But, you can do the following:
Iterate through the array. If the color is less or equal to 888888, change it to 000000. If it's greater, change it to FFFFFF.
Create a new [7][5] array.
Iterate through again. Add the values of each pixel (black = 0, white = 1) in any given 35th of the array. The sample size will depend entirely on the size of your original array2d. If the average for that block is greater than or equal to 17.5, add a white element to your matrix. If it's less than 17.5, add a black element to your matrix.
I'm not explicitly familiar with the call to pygame.surfarray.array2d(). However, since you're going from a binary color layout to a smaller binary color matrix, you can subdivide the original image using your new proportions in order to properly color the resulting square. I'll give an example.
Say your initial image is 14x10 and you wish to have a 7x5 matrix. Your initial image looks like this:
[[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,0,1,1,1,0,0,0,0,0],
[0,0,0,0,0,1,1,0,1,1,0,0,0,0],
[0,0,0,0,1,1,1,1,1,1,1,0,0,0],
[0,0,0,1,1,1,1,1,1,1,1,1,0,0],
[0,0,1,1,0,0,0,0,0,0,0,1,1,0],
[0,1,1,0,0,0,0,0,0,0,0,0,1,1]]
What you need to do is divide x-wise by 7, and y-wise by 5. Since I picked nice numbers, the slices of the large image you'll be looking at will be 2x2. Take the top left 2x2 block, for example:
[[0,0],
[0,0]] -> [0]
This mini-matrix maps to a single pixel of your 7x5 image. Obviously, in this case it will be 0. Let's look at the bottom right:
[[1,0],
[1,1]] -> [1]
This will map to a value of 1 in your 7x5 image. As you can see, the tricky case in this example is when you have equal 1s and 0s. This will not be a huge issue, fortunately, as your initial image is always at least 100x100.
Applying this method to my example, the shrunk 7x5 image looks like this:
[[0,0,0,0,0,0,0],
[0,0,0,0,0,0,0],
[0,0,0,1,1,0,0],
[0,0,1,1,1,1,0],
[0,1,0,0,0,0,1]]
Psuedocode steps:
Find the size of the mini-matrices (divide by 5 and 7). This will work with an image of any size larger than 7x5.
For each mini-matrix, count the black and white spaces (0 and 1).
Decide whether the space in your final 7x5 matrix should be black or white. In my example, I say that the final space should be black if (number of white squares >= number of black squares). I'm worried that using this will cause problems for you because your pen size is relatively thin compared to the size of your 7x5 divisions. If this is a problem, try something like if (number of white squares * 2 >= number of black squares). This effectively weights the white squares more.
I'm happy to elaborate on this psuedocode. Just let me know.
Finally, if you are still having issues, I might try using a size larger than 7x5. It will give you more precision at a cost to your OCR algorithm. Good luck.

Categories