Image Processing Outlines - python

I'm writing a program that does basic image processing.
Keep in mind that the images are in grayscale, not RGB, also, I'm fairly new to Python, so an explanation of what I'm doing wrong/right would be incredibly helpful.
I'm trying to write an outline algorithm that follows this set of rules:
All light pixels in the original must be white in the outline image.
All dark pixels on the edges of the image must be black in the outline image.
If a pixel that is not on an edge of the image is dark and all of the 8 surrounding pixels are dark, this pixel is on the inside of a shape and must be white in the outline image.
All other dark pixels must be black in the outline image.
So far I have this:
def outlines(image):
"""
Finds the outlines of shapes in an image. The parameter must be
a two-dimensional list of pixels. The return value is another
two-dimensional list of pixels which describes an image showing
outlines of the shapes in the original image. Each pixel in the
return value will be either black (0) or white (255).
"""
height=len(image)
width=len(image[0])
new_image=[]
for r in range(height):
new_row=[]
index=0
for c in range(width):
if image[r][c]>128:
new_row.append(255)
if image[r][c]<=128:
new_row.append(0])
new_image.append(new_row)
Can someone show me how to implement the algorithm into my outlines function?
Thanks in advance.
Edit: This is an assignment for my University Comp Sci class, I'm not asking for someone to do my homework, rather because I've virtually no idea what the next step is.
Edit2: If someone could explain to me a simple edge detection function that is similar to the algorithm I need to create I would appreciate it.

In addition to check if your pixel is dark or clear, you should also check, when dark, if the rest of pixels around are also dark in order to make that point white instead.
Check this function and try to use it for that purpose:
def all_are_dark_around(image, r, c):
# range gives the bounds [-1, 0, 1]
# you could use the list directly. Probably better for this especific case
for i in range(-1,2):
for j in range(-1,2):
# if the pixel is clear return False.
# note that image[r+0][c+0] is always dark by definition
if image[r+i][c+j] <= 128:
return False
#the loop finished -> all pixels in the 3x3 square were dark
return True
Advices:
note that you should never check image[r][c] when r or c are equal
to 0 or to height or width. That is, when the checked pixel is in
the border because in this case there is at least one side where
there is no adjacent pixel to look at in the image and you will get
an IndexError
don't expect this code to work directly and to be the best code on
terms of efficiency or good style. This is a hint for your homework.
You should do it. So look at the code, take your time (optimally it
should be equal or longer than the time it took to me to write the
function), understand how it works and adapt it to your code fixing
any exceptions and border situations you encounter in the way.

Related

Image Positioning OpenCV

This may be called "Region of Interest" I'm not exactly sure. But, what I'd like to do is rather easy to explain.
I have a photo that I need to align to a grid.
https://snag.gy/YaAWdg.jpg
For starters; the little text that says "here" must be 151px from the top of the screen.
Next; from "here" to position 8 of the chin must be 631px
Finally; a straight line down the middle of the picture at line 28 on the nose must be done.
If i'm not making sense please tell me to elaborate.
I have the following ideas (this is pseudo code)
It is simply to loop until the requirements are met with a resize function, a lot like brute forcing but thats all i can think of..
i.e..
while (top.x,y = 151,0)
img.top-=1 ## this puts the image one pixel above until reaching the desired positioning
while (top.x,y & eight.x,y != 631)
resize += 1 # resize by 1 pixel until the height is reached
## center the nose
image.position = nose.
Consider switching the order of your operations to prevent the need to iterate. A little bit of math and a change of perspective should do the trick:
1.) Resize the image such that the distance from "here" to the chin is 631px.
2.) Use a region of interest to crop your image so that "here" is 151px from the top of the screen.
3.) Draw your line.
EDIT:
The affine transform in OpenCV would work to morph your image into the proper fill, assuming that you have all the proper constraints defined.
If all you need to do is a simple scale... First calculate the distance between points, using something like this.
Point2f a(10,10);
Point2f b(100,100);
float euclideanDist(Point& p, Point& q) {
Point diff = p - q;
return cv::sqrt(diff.x*diff.x + diff.y*diff.y);
}
Then create a scale factor to resize your image
float scaleFactor = euclideanDist(a,b) / 631;
cv::resize(input, output, cv::Size(), scaleFactor, scaleFactor, cv::INTER_LINEAR);
Using both instances of scaleFactor will create a uniform scaling in X&Y. Using two different scale factors will scale X and Y independently.
Take a look to OpenCV's tutorial, for images with faces you can use Haar Cascades to simplify the work. (https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html#face-detection)
Otherwise, look at ROI (Region of Interest) to extract an area and apply your algorithm on it (resize or crop)

Algorithm for detecting edge of shape in image with PIL in python when separation < 1 px

This is kind of an easy problem to solve, i'm trying to re-arrange shapes in a plane but first i need to detect them in the right way, i've came up with this very inefficient algorithm but it does the job fine until it reaches two shapes that are separated by a distance that is < 1px:
Here you have it in python pseudocode:
#all pixels
for x in range(0, image.width):
for y in range(0, image.height):
if pixel is black:
# mark start of shapes
else:
if shape is open:
for r in range (0, image.height):
if pixel is black:
# found shape, keep shape open
else:
# close shape
else:
for r in range (0, image.height):
paint pixel gray # this draws the vertical gray lines in the example
This is the resulting image:
As you can see, the grey bars are drawn between the shapes but it doesn't work when two shapes are too close together (with a distance of less than 1px between each other)
IMPORTANT: I don't need to make this work for shapes that overlap vertically.
I dont really care about the python/pillow syntax if you can explain really well what your algorithm does and how it works and it looks like python / PIL code.
It sounds like you want to look at both the current column of pixels, and the previous one. If there's no y-position that's black in both columns, it's a new shape (or no shape).

Uniquely identifying clustered ellipses which are about the same shape, but different sizes

I'm working on an image of parasite eggs. I need to identify as many eggs as I can individually, I've managed to decent borders around them using the watershed algorithm, but I want to look at the oval egg as a whole, rather than just the yolk part which is generally what the watershed algorithm picks out. I have tried ellipse detection but it only gets the water bubbles (the round black bits with a white dot in the middle) which I actually want to ignore (but am planning to implement later).
What would be the best method to achieve this, I've looked into image mapping but it only returns the best match, I've also considered shape recognition but it apparently isn't implemented in OpenCV.
Here is what my result I get using the watershed algorithm:
http://imgur.com/7ptyIOL
And here is an idea of what I'm wanting to get: http://imgur.com/XPLUixa
All right I've been hacking away at it today and this is the solution I've found. I threshold the image, and then I find the contours. After I have found the contours I will draw an ellipse around each of them if it passes a little check function I have made. This currently works quite well, but I will need to improve my method of thresholding the image before I can expect any real progress.
This code generates the contours and draws them
_, contours, hierarchy = cv2.findContours(opening.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
#will give an error if there are less than 5 points
if len(c) > 4:
elip = cv2.fitEllipse(c)
if check_ellipse(elip):
cv2.ellipse(img, elip, (255, 0, 0))
This is the function used to check the correctness
def check_ellipse(ellipse):
h = ellipse[1][0]
w = ellipse[1][1]
#make sure it's not too large, or too circular
if abs(h-w) < 2 or h > 30 or w > 30:
return False
return True
Later on I may change this so it first gets rid off all of the ellipses which don't pass the test, that way I can have a list of their coordinates which is what I really need if I want to progress with this program.

guessing the rgb gradient from a map?

I have a map with a scale like this one: (the numbers are just an example)
which describes a single variable on a map. However, I don't have access to the original data and know pretty close to nothing
about image processing. What I have done is use PIL to get the pixel-coordinates and RGB values of each point on the map. Simply using pix = im.load() and saving pix[x,y] for each x,y. Now I would like to guess the value of each point using the gradient above.
Is there a standard formula for such a gradient? Does it look very familiar to the trained eye? I have visited Digital Library of Mathematical Functions for some examples ... but I'm not sure if it's using the hue, the rgb height function or something else (to make things easier I'm also colorblind to some greens/brows/reds) :)
Any tips on how to proceed, libraries, links or ideas are appreciated. Thank you!
edit:
Following the replies and martineau's suggestion, I've tried to catch the colors at the top and bottom:
def rgb2hls(colotup):
'''converts 225 based RGB to 360 based HLS
`input`: (222,98,32) tuple'''
dec_rgb = [x/255.0 for x in colotup] # use decimal 0.0 - 1.0 notation for RGB
hsl_col = colorsys.rgb_to_hls(dec_rgb[0], dec_rgb[1], dec_rgb[2])
# PIL uses hsl(360,x%,y%) notation and throws errors on float, so I use int
return (int(hsl_col[0]*360), int(hsl_col[1]*100), int(hsl_col[2]*100))
def pil_hsl_string(hsltup):
'''returns a string PIL can us as HSL color
from a tuple (x,y,z) -> "hsl(x,y%,z%)"'''
return 'hsl(%s,%s%%,%s%%)' % (hsltup[0], hsltup[1], hsltup[2])
BottomRed = (222,98,32) # taken with gimp
TopBlue = (65, 24, 213)
hue_red = pil_hsl_string(rgb2hls(BottomRed))
hue_blue = pil_hsl_string(rgb2hls(TopBlue))
However they come out pretty different ... which makes me worry about using the rgb_to_hls function to extract the values. Or I'm I doing something very wrong? Here's what the color s convert to with the code:
Interesting question..
If you do a clock-wise walk in HSL color-space from 250,85%,85% --> 21,85%,85% you get a gradient very close to the one you've shown. The obvious difference being that your image exhibits a fairly narrow band of greenish values.
So, if you have the 4 magic numbers then you can interpolate to any point within the map.
These of course being the first and last colour, also the first and last scale value.
Here's the image I got with a straight linear gradient on the H channel (used the gimp).
EDIT: I've since whipped up a program to grab the pixel values for each row, graphing the results. You can see that indeed, the Hue isn't linear, you can also see the S & V channels taking a definite dip at around 115 (115 pixels from top of image) This indeed corresponds with the green band.
Given the shape of the curves, I'm inclined to think that perhaps they are intended to model something. But don't have the experience in related fields to recognise the shape of the curves.
Below, I've added the graphs for the change in both the HSV and RGB models.
The left of the graph represents the top of the bar.
The X-axis labels represent pixels
Quite interesting, me thinks. Bookmarked.
The scale in the image looks like an HSV gradient to me, something like what is mentioned in this question. If so, you could use the colorsys.rgb_to_hls() or colorsys.rgb_to_hsv() functions to obtain a hue color value between 0 and 1 from the r,g,b values in a pixel. That can then be mapped accordingly.
However, short of doing OCR, I have no idea how to determine the range of values being represented unless it's some consistent range you can just hardcode.
I would recomend to define an area where you want to compare the colour. Take an FFT of the regions. Each colour is defined by a frequency. You do the same on the countour scale. then compare and narrow on a value.
I have found some like to understand it better.
http://www.imagemagick.org/Usage/fourier/
You can get something like that by varying the hue with a fixed saturation and luminance.
http://en.wikipedia.org/wiki/HSL_and_HSV

Image Spiral Pixel Search

I'm trying to figure out how to do a pixel (color) search from the center and out, in a spiral shape.. Not like the normal "left to right pixel search".
So far i've made some simple x-y searches. Using standard PIL. But it was to slow. as the result always seems to be closer to the center (of the image) in my case. The thing is that it's not a square image, so the center position(s) can be 2 or more pixels (not A center, but "two"+ pixels centerd), this is where I "loose it".. Can you peeps give me some hints? Im always working from a screenshot PIL-> ImageGrab.grab(), using image.size to get the image size, and px=image.getpixel((x, y)) to get the current pixel-location
I'm working with R,G,B-colours: if px[0] == r and px[1] == g and px[2] == b:
See this answer for a bunch of different algorithms written in python for iterating over a matrix in a spiral fashion.

Categories

Resources