Initiates repo, creates led_detector and region growing

This commit is contained in:
2012-06-19 16:20:21 +02:00
parent d1f82d2f44
commit e4711084b8
29 changed files with 694 additions and 0 deletions

17
LedDetector/.project Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>LedDetector</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

10
LedDetector/.pydevproject Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?>
<pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/LedDetector</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

4
LedDetector/README.md Normal file
View File

@@ -0,0 +1,4 @@
Led_detector
================
Automatic Led detector on image. Should answer this SO question : http://goo.gl/iQfcg

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

View File

@@ -0,0 +1,227 @@
'''
Created on 25 mai 2012
@author: jlengrand
'''
import sys
import cv
# ---- Useful functions ----
def init_video(video_file):
"""
Given the name of the video, prepares the stream and checks that everything works as attended
"""
capture = cv.CaptureFromFile(video_file)
nFrames = int( cv.GetCaptureProperty( capture, cv.CV_CAP_PROP_FRAME_COUNT ) )
fps = cv.GetCaptureProperty( capture, cv.CV_CAP_PROP_FPS )
if fps != 0:
waitPerFrameInMillisec = int( 1/fps * 1000/1 )
print 'Num. Frames = ', nFrames
print 'Frame Rate = ', fps, ' frames per sec'
print '----'
return capture
else:
return None
def display_img(img, delay=1000):
"""
One liner that displays the given image on screen
"""
cv.NamedWindow("Vid", cv.CV_WINDOW_AUTOSIZE)
cv.ShowImage("Vid", img)
cv.WaitKey(delay)
def display_video(my_video, frame_inc=100, delay=100):
"""
Displays frames of the video in a dumb way.
Used to see if everything is working fine
my_video = cvCapture object
frame_inc = Nmber of increments between each frame displayed
delay = time delay between each image
"""
cpt = 0
img = cv.QueryFrame(my_video)
if img != None:
cv.NamedWindow("Vid", cv.CV_WINDOW_AUTOSIZE)
else:
return None
nFrames = int( cv.GetCaptureProperty( my_video, cv.CV_CAP_PROP_FRAME_COUNT ) )
while cpt < nFrames:
for ii in range(frame_inc):
img = cv.QueryFrame(my_video)
cpt + 1
cv.ShowImage("Vid", img)
cv.WaitKey(delay)
def grab_images(video_file, frame_inc=100, delay = 100):
"""
Walks through the entire video and save image for each increment
"""
my_video = init_video(video_file)
if my_video != None:
# Display the video and save evry increment frames
cpt = 0
img = cv.QueryFrame(my_video)
if img != None:
cv.NamedWindow("Vid", cv.CV_WINDOW_AUTOSIZE)
else:
return None
nFrames = int( cv.GetCaptureProperty( my_video, cv.CV_CAP_PROP_FRAME_COUNT ) )
while cpt < nFrames:
for ii in range(frame_inc):
img = cv.QueryFrame(my_video)
cpt += 1
cv.ShowImage("Vid", img)
out_name = "data/output/" + str(cpt) + ".jpg"
cv.SaveImage(out_name, img)
print out_name, str(nFrames)
cv.WaitKey(delay)
else:
return None
def to_gray(img):
"""
Converts the input in grey levels
Returns a one channel image
"""
grey_img = cv.CreateImage(cv.GetSize(img), img.depth, 1)
cv.CvtColor(img, grey_img, cv.CV_RGB2GRAY )
return grey_img
def grey_histogram(img, nBins=64):
"""
Returns a one dimension histogram for the given image
The image is expected to have one channel, 8 bits depth
nBins can be defined between 1 and 255
"""
hist_size = [nBins]
h_ranges = [0, 255]
hist = cv.CreateHist(hist_size , cv.CV_HIST_ARRAY, [[0, 255]], 1)
cv.CalcHist([img], hist)
return hist
def extract_bright(grey_img, histogram=False):
"""
Extracts brightest part of the image.
Expected to be the LEDs (provided that there is a dark background)
Returns a Thresholded image
histgram defines if we use the hist calculation to find the best margin
"""
## Searches for image maximum (brightest pixel)
# We expect the LEDs to be brighter than the rest of the image
[minVal, maxVal, minLoc, maxLoc] = cv.MinMaxLoc(grey_img)
print "Brightest pixel val is %d" %(maxVal)
#We retrieve only the brightest part of the image
# Here is use a fixed margin (80%), but you can use hist to enhance this one
if 0:
## Histogram may be used to wisely define the margin
# We expect a huge spike corresponding to the mean of the background
# and another smaller spike of bright values (the LEDs)
hist = grey_histogram(img, nBins=64)
[hminValue, hmaxValue, hminIdx, hmaxIdx] = cv.GetMinMaxHistValue(hist)
margin = 0# statistics to be calculated using hist data
else:
margin = 0.8
thresh = int( maxVal * margin) # in pix value to be extracted
print "Threshold is defined as %d" %(thresh)
thresh_img = cv.CreateImage(cv.GetSize(img), img.depth, 1)
cv.Threshold(grey_img, thresh_img , thresh, 255, cv.CV_THRESH_BINARY)
return thresh_img
def find_leds(thresh_img):
"""
Given a binary image showing the brightest pixels in an image,
returns a result image, displaying found leds in a rectangle
"""
contours = cv.FindContours(thresh_img,
cv.CreateMemStorage(),
mode=cv.CV_RETR_EXTERNAL ,
method=cv.CV_CHAIN_APPROX_NONE ,
offset=(0, 0))
regions = []
while contours:
pts = [ pt for pt in contours ]
x, y = zip(*pts)
min_x, min_y = min(x), min(y)
width, height = max(x) - min_x + 1, max(y) - min_y + 1
regions.append((min_x, min_y, width, height))
contours = contours.h_next()
out_img = cv.CreateImage(cv.GetSize(grey_img), 8, 3)
for x,y,width,height in regions:
pt1 = x,y
pt2 = x+width,y+height
color = (0,0,255,0)
cv.Rectangle(out_img, pt1, pt2, color, 2)
return out_img, regions
def leds_positions(regions):
"""
Function using the regions in input to calculate the position of found leds
"""
centers = []
for x, y, width, height in regions:
centers.append( [x+ (width / 2),y + (height / 2)])
return centers
if __name__ == '__main__':
video_file = "data/MusicLEDBox.avi"
if 0:
# do once once, create some images out of the video
grab_images(video_file, frame_inc=100, delay = 100)
img = cv.LoadImage("data/output/600.jpg")
if img != None:
# Displays the image I ll be working with
display_img(img, delay = 100)
else:
print "IMG not found !"
sys.exit(0)
####
# Starts image processing here
####
# Turns to one channel image
grey_img = to_gray(img)
display_img(grey_img, 1000)
# Detect brightest point in image :
thresh_img = extract_bright(grey_img)
display_img(thresh_img, delay = 1000)
# We want to extract the elements left, and count their number
led_img, regions = find_leds(thresh_img)
display_img(led_img, delay=1000)
centers = leds_positions(regions)
print "Total number of Leds found : %d !" %(len(centers))
print "###"
print "Led positions :"
for c in centers:
print "x : %d; y : %d" %(c[0], c[1])
print "###"

View File

View File

@@ -0,0 +1,163 @@
'''
Created on 24 mai 2012
@author: jlengrand
'''
import cv
def init_video(video_file):
"""
Given the name of the video, prepares the flux and checks that everything works as attended
"""
capture = cv.CaptureFromFile(video_file)
nFrames = int( cv.GetCaptureProperty( capture, cv.CV_CAP_PROP_FRAME_COUNT ) )
fps = cv.GetCaptureProperty( capture, cv.CV_CAP_PROP_FPS )
if fps != 0:
waitPerFrameInMillisec = int( 1/fps * 1000/1 )
print 'Num. Frames = ', nFrames
print 'Frame Rate = ', fps, ' frames per sec'
print '----'
return capture
else:
return None
def display_img(img, delay=1000):
"""
One liner that displays the given image on screen
"""
cv.NamedWindow("Vid", cv.CV_WINDOW_AUTOSIZE)
cv.ShowImage("Vid", img)
cv.WaitKey(delay)
def display_video(my_video, frame_inc=100, delay=100):
"""
Displays frames of the video in a dumb way.
Used to see if everything is working fine
my_video = cvCapture object
frame_inc = Nmber of increments between each frame displayed
delay = time delay between each image
"""
cpt = 0
img = cv.QueryFrame(my_video)
if img != None:
cv.NamedWindow("Vid", cv.CV_WINDOW_AUTOSIZE)
else:
return None
nFrames = int( cv.GetCaptureProperty( my_video, cv.CV_CAP_PROP_FRAME_COUNT ) )
while cpt < nFrames:
for ii in range(frame_inc):
img = cv.QueryFrame(my_video)
cpt + 1
cv.ShowImage("Vid", img)
cv.WaitKey(delay)
def grab_images(video_file, frame_inc=100, delay = 100):
"""
Walks through the entire video and save image for each increment
"""
my_video = init_video(video_file)
if my_video != None:
# Display the video and save evry increment frames
cpt = 0
img = cv.QueryFrame(my_video)
if img != None:
cv.NamedWindow("Vid", cv.CV_WINDOW_AUTOSIZE)
else:
return None
nFrames = int( cv.GetCaptureProperty( my_video, cv.CV_CAP_PROP_FRAME_COUNT ) )
while cpt < nFrames:
for ii in range(frame_inc):
img = cv.QueryFrame(my_video)
cpt += 1
cv.ShowImage("Vid", img)
out_name = "data/output/" + str(cpt) + ".jpg"
cv.SaveImage(out_name, img)
print out_name, str(nFrames)
cv.WaitKey(delay)
else:
return None
if __name__ == '__main__':
video_file = "data/MusicLEDBox.avi"
if 0:
# do once once, create some images out of the video
grab_images(video_file, frame_inc=100, delay = 100)
img = cv.LoadImage("data/output/600.jpg")
if img != None:
# Displays the image I ll be working with
display_img(img, delay = 100)
else:
print "IMG not found !"
####
# Start processing here
####
# Turns to one channel image
grey_img = cv.CreateImage(cv.GetSize(img), img.depth, 1)
cv.CvtColor(img, grey_img, cv.CV_RGB2GRAY )
display_img(grey_img, 100)
# Detect brightest point in image :
## USed to calculate max of histogram
#hist_size = [64]
#h_ranges = [0, 255]
#hist = cv.CreateHist([64] , cv.CV_HIST_ARRAY, [[0, 255]], 1)
#cv.CalcHist([grey_img], hist)
#[minValue, maxValue, minIdx, maxIdx] = cv.GetMinMaxHistValue(hist)
#print minValue, maxValue, minIdx, maxIdx
[minVal, maxVal, minLoc, maxLoc] = cv.MinMaxLoc(grey_img)
print minVal, maxVal, minLoc, maxLoc
# could use histogram here to find where to stop
# Threshold at 80%
margin = 0.8
thresh = int( maxVal * margin)
print thresh
thresh_img = cv.CreateImage(cv.GetSize(img), img.depth, 1)
cv.Threshold(grey_img, thresh_img , thresh, 255, cv.CV_THRESH_BINARY)
display_img(thresh_img, delay = 100)
# Want to get the number of points now
contours = cv.FindContours(thresh_img,
cv.CreateMemStorage(),
mode=cv.CV_RETR_EXTERNAL ,
method=cv.CV_CHAIN_APPROX_NONE ,
offset=(0, 0))
regions = []
while contours:
pts = [ pt for pt in contours ]
x, y = zip(*pts)
min_x, min_y = min(x), min(y)
width, height = max(x) - min_x + 1, max(y) - min_y + 1
regions.append((min_x, min_y, width, height))
contours = contours.h_next()
out_img = cv.CreateImage(cv.GetSize(grey_img), 8, 3)
for x,y,width,height in regions:
pt1 = x,y
pt2 = x+width,y+height
color = (0,0,255,0)
cv.Rectangle(out_img, pt1, pt2, color, 2)
print len(regions)
display_img(out_img)
print "Finished!"

4
region_growing/README.md Normal file
View File

@@ -0,0 +1,4 @@
region_growing
================
Sample showing an example of simple region growing algorithm

View File

@@ -0,0 +1,107 @@
'''
Contains all basic functions, such as image creation, copy, . . .
No Image processing complex algorithms here.
Created on Nov 19, 2011
@author: Julien Lengrand-Lambert
@email: julien@lengrand.fr
'''
import sys
import cv
_mouse_pos = []
def _on_mouse(event, x, y, flags, param):
"""
None = onMouse(event, x, y, flags, param)
Function automatically called by opencv when having mouse events on a
displayed frame. In here, we are searching for a left mouse click
"""
global _mouse_pos
if event == cv.CV_EVENT_LBUTTONDOWN :
_mouse_pos.append((x, y))
def mouse_point(img, name="Mouse Points", mode="S"):
"""
Displays input image and waits for user click on a point of the image.
For each click, the (x, y) coordinates are retrieved.
Two modes are possible:
-Single :
The function exits automatically on first click.
Returns a list containing 1 (x,y) tuple.
-Multiple :
All clicks are saved. The function exists when the user types on keyboard
Returns containing (x,y) tuples.
---
Ex:
img = cv.LoadImage("../data/tippy.jpg", cv.CV_LOAD_IMAGE_GRAYSCALE)
mouse_points(img, name="mouse points", mode="S")
"""
if not isinstance(name, str):
raise TypeError("(%s) Name :String expected!" % (sys._getframe().f_code.co_name))
if not isinstance(mode, str):
raise TypeError("(%s) Mode :String expected!" % (sys._getframe().f_code.co_name))
global _mouse_pos
cv.StartWindowThread()
cv.NamedWindow(name, cv.CV_WINDOW_AUTOSIZE)
cv.SetMouseCallback(name, _on_mouse)
try :
cv.ShowImage(name, img)
except TypeError:
raise TypeError("(%s) img : IplImage expected!" % (sys._getframe().f_code.co_name))
if mode == "S":
_single_point()
elif mode == "M":
_multiple_points()
else:
raise ValueError("(%s) Mode can be either S (single) or M (multiple)" % (sys._getframe().f_code.co_name))
cv.DestroyWindow(name)
return _mouse_pos
def _single_point():
"""
Internal function, used in mouse_point
"""
while(len(_mouse_pos) == 0):
cv.WaitKey(10)
def _multiple_points():
"""
Internal function, used in mouse_point
"""
char = -1
while (char == -1):
char = cv.WaitKey(10)
# ----
def create_same_image(in_img, zero=0):
"""
Creates an image of same size, depth and number of channels as input image
If zero is activated, the image content is set to 0 to avoid parasites.
---
Ex:
img = cv.LoadImage("../data/tippy.jpg", cv.CV_LOAD_IMAGE_GRAYSCALE)
copy_img = create_same_image(img, 0)
"""
try :
out_img = cv.CreateImage(cv.GetSize(in_img),
in_img.depth,
in_img.nChannels)
except TypeError:
raise TypeError("(%s) Image creation failed!" % (sys._getframe().f_code.co_name))
if zero:
cv.Zero(out_img)
return out_img

BIN
region_growing/data/gnu.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,38 @@
'''
Contains all functions related to graphical display.
Created on Nov 19, 2011
@author: Julien Lengrand-Lambert
@email: julien@lengrand.fr
'''
import sys
import cv
def display_single_image(img, name="Image", x_pos=0, y_pos=0, delay=0):
"""
Displays an image on screen.
Position can be chosen, and time on screen too.
Delay corresponds to the display time, in milliseconds.
If delay =0, the Image stays on screen until user interaction.
----
Ex:
img = cv.LoadImage("../data/tippy.jpg", cv.CV_LOAD_IMAGE_GRAYSCALE)
display_single_image(img, "My Tippy", 0, 0, 100)
"""
#TODO: Still not implemented!
if not isinstance(name, str):
raise TypeError("(%s) Name :String expected!" % (sys._getframe().f_code.co_name))
if (not isinstance(x_pos, int)) \
or (not isinstance(x_pos, int)) \
or (not isinstance(x_pos, int)) :
raise TypeError("(%s) Int expected!" % (sys._getframe().f_code.co_name))
cv.StartWindowThread()
cv.NamedWindow(name)
cv.MoveWindow(name, x_pos, y_pos)
cv.ShowImage(name, img)
cv.WaitKey(delay)
cv.DestroyWindow(name)

View File

@@ -0,0 +1,104 @@
'''
This method contains all functions segmentation related.
Created on Nov 26, 2011
@author: Julien Lengrand-Lambert
@email: julien@lengrand.fr
'''
import sys
import cv
import numpy
def simple_region_growing(img, seed, threshold=1, conn=4):
"""
A (very) simple implementation of region growing.
Extracts a region of the input image depending on a start position and a stop condition.
The input should be a single channel 8 bits image and the seed a pixel position (x, y).
The threshold corresponds to the difference between outside pixel intensity and mean intensity of region.
In case no new pixel is found, the growing stops.
The connectivity can be set to 4 or 8. 4-connectivity is taken by default, and 8-connectiviy requires most computations.
Outputs a single channel 8 bits binary (0 or 255) image. Extracted region is highlighted in white.
"""
try:
dims = cv.GetSize(img)
except TypeError:
raise TypeError("(%s) img : IplImage expected!" % (sys._getframe().f_code.co_name))
# img test
if not(img.depth == cv.IPL_DEPTH_8U):
raise TypeError("(%s) 8U image expected!" % (sys._getframe().f_code.co_name))
elif not(img.nChannels is 1):
raise TypeError("(%s) 1C image expected!" % (sys._getframe().f_code.co_name))
# threshold tests
if (not isinstance(threshold, int)) :
raise TypeError("(%s) Int expected!" % (sys._getframe().f_code.co_name))
elif threshold < 0:
raise ValueError("(%s) Positive value expected!" % (sys._getframe().f_code.co_name))
# seed tests
if not((isinstance(seed, tuple)) and (len(seed) is 2) ) :
raise TypeError("(%s) (x, y) variable expected!" % (sys._getframe().f_code.co_name))
if (seed[0] or seed[1] ) < 0 :
raise ValueError("(%s) Seed should have positive values!" % (sys._getframe().f_code.co_name))
elif ((seed[0] > dims[0]) or (seed[1] > dims[1])):
raise ValueError("(%s) Seed values greater than img size!" % (sys._getframe().f_code.co_name))
# Connectivity tests
if (not isinstance(conn, int)) :
raise TypeError("(%s) Int expected!" % (sys._getframe().f_code.co_name))
if conn == 4:
orient = [(1, 0), (0, 1), (-1, 0), (0, -1)] # 4 connectivity
elif conn == 8:
orient = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)] # 8 connectivity
else:
raise ValueError("(%s) Connectivity type not known (4 or 8 available)!" % (sys._getframe().f_code.co_name))
reg = cv.CreateImage( dims, cv.IPL_DEPTH_8U, 1)
cv.Zero(reg)
#parameters
mean_reg = float(img[seed[1], seed[0]])
size = 1
pix_area = dims[0]*dims[1]
contour = [] # will be [ [[x1, y1], val1],..., [[xn, yn], valn] ]
contour_val = []
dist = 0
cur_pix = [seed[0], seed[1]]
#Spreading
while(dist<threshold and size<pix_area):
#adding pixels
for j in range(len(orient)):
#select new candidate
temp_pix = [cur_pix[0] +orient[j][0], cur_pix[1] +orient[j][1]]
#check if it belongs to the image
is_in_img = dims[0]>temp_pix[0]>0 and dims[1]>temp_pix[1]>0 #returns boolean
#candidate is taken if not already selected before
if (is_in_img and (reg[temp_pix[1], temp_pix[0]]==0)):
contour.append(temp_pix)
contour_val.append(img[temp_pix[1], temp_pix[0]] )
reg[temp_pix[1], temp_pix[0]] = 150
#add the nearest pixel of the contour in it
dist = abs(int(numpy.mean(contour_val)) - mean_reg)
dist_list = [abs(i - mean_reg) for i in contour_val ]
dist = min(dist_list) #get min distance
index = dist_list.index(min(dist_list)) #mean distance index
size += 1 # updating region size
reg[cur_pix[1], cur_pix[0]] = 255
#updating mean MUST BE FLOAT
mean_reg = (mean_reg*size + float(contour_val[index]))/(size+1)
#updating seed
cur_pix = contour[index]
#removing pixel from neigborhood
del contour[index]
del contour_val[index]
return reg

20
region_growing/test.py Normal file
View File

@@ -0,0 +1,20 @@
import cv
import segmentations as se
import basic_operations as bo
import display_operations as do
user_input = 0
img_name = "data/gnu.jpg"
threshold = 20
img = cv.LoadImage(img_name, cv.CV_LOAD_IMAGE_GRAYSCALE)
if user_input:
seed = bo.mouse_point(img, mode="S") # waits for user click to get seed
else:
seed = (70, 106)
out_img = se.simple_region_growing(img, seed, threshold)
do.display_single_image(out_img, "Region Growing result")