I am making a stitching with opencv and Python. All works well, except one thing : I don't manage to compute the exact final size of the result picture.
My image is always too big and i have black border. Moreover, the offset doesn't seem to be correct because there is a black line where pictures have merged.
Here is my function :
def calculate_size(size_image1, size_image2, homography):
## Calculate the size and offset of the stitched panorama.
offset = abs((homography*(size_image2[0]-1,size_image2[1]-1,1))[0:2,2])
print offset
size = (size_image1[1] + int(offset[0]), size_image1[0] + int(offset[1]))
if (homography*(0,0,1))[0][1] > 0:
offset[0] = 0
if (homography*(0,0,1))[1][2] > 0:
offset[1] = 0
## Update the homography to shift by the offset
homography[0:2,2] += offset
return (size, offset)
## 4. Combine images into a panorama. [4] --------------------------------
def merge_images(image1, image2, homography, size, offset, keypoints):
## Combine the two images into one.
panorama = cv2.warpPerspective(image2,homography,size)
(h1, w1) = image1.shape[:2]
for h in range(h1):
for w in range(w1):
if image1[h][w][0] != 0 or image1[h][w][3] != 0 or image1[h][w][4] != 0:
panorama[h+offset[1]][w + offset[0]] = image1[h][w]
## TODO: Draw the common feature keypoints.
return panorama
And my results:
1st image :
2nd image :
Stitched image :
What am I doing wrong?
if (homography*(0,0,1))[0][1] > 0:
offset[0] = 0
if (homography*(0,0,1))[1][2] > 0:
offset[1] = 0
Your code is wrong.The right one as following:
if (homography*(0,0,1))[0][2] > 0:
offset[0] = 0
if (homography*(0,0,1))[1][2] > 0:
offset[1] = 0
Well, I don't know a lot about Python but basically I had the some problem.
To solve the size issues I did the following:
perspectiveTransform( obj_original_corners, scene_corners, homography);
After that, I just searched in both images the smallest_X, smallest_Y, biggest_X and biggest_Y.
These numbers I then used in:
cv::warpPerspective(img_2,WarpedImage,homography,cv::Size(biggestX-smallestX,biggestY-smallestY));
So in that case the new image itself will have the proper size even if the 2nd image has a negative x or negative y.
Only thing I'm still struggling with myself at this moment is how to apply the shift to warpPerspective because now part of my image is cutoff due to negative numbers.
Accordding to stitching,All your process are right.The result is because your source picture.
for h in range(h1):
for w in range(w1):
if image1[h][w][0] != 0 or image1[h][w][3] != 0 or image1[h][w][4] != 0:
panorama[h+offset[1]][w + offset[0]] = image1[h][w]
The operation only filter the pixel ,whose color is zero.In fact ,some pixel seems like black,but it is not pure black and very near black. So these seem black pixel will not filter out by your program.
Related
I'm trying to filter out short lines from my canny edge detection. Here's what I'm currently using as well as a brief explanation:
I start out by taking a single channel of the image and running CV2's Canny edge detection. Following that, I scan through each pixel and detect if there are any around it that are white (True, 255). If it is, I add it to a group of true pixels and then check every pixel around it (and keep looping until there are no white/True pixels left. I then replace all the pixels with black/False if the group count is less than a designated threshold (In this case, 100 pixels).
While this works (as shown below) it's awfully slow. I'm wondering if there's a faster, easier way to do this.
import cv2
img = cv2.imread("edtest.jpg")
img_r = img.copy()
img_r[:, :, 0] = 0
img_r[:, :, 1] = 0
img_r = cv2.GaussianBlur(img_r, (3, 3), 0)
basic_edge = cv2.Canny(img_r, 240, 250)
culled_edge = basic_edge.copy()
min_threshold = 100
for x in range(len(culled_edge)):
print(x)
for y in range(len(culled_edge[x])):
test_pixels = [(x, y)]
true_pixels = [(x, y)]
while len(test_pixels) != 0:
xorigin = test_pixels[0][0]
yorigin = test_pixels[0][1]
if 0 < xorigin < len(culled_edge) - 1 and 0 < yorigin < len(culled_edge[0]) - 1:
for testx in range(3):
for testy in range(3):
if culled_edge[xorigin-1+testx][yorigin - 1 + testy] == 255 and (xorigin-1+testx, yorigin-1+testy) not in true_pixels:
test_pixels.append((xorigin-1+testx, yorigin-1+testy))
true_pixels.append((xorigin-1+testx, yorigin-1+testy))
test_pixels.pop(0)
if 1 < len(true_pixels) < min_threshold:
for i in range(len(true_pixels)):
culled_edge[true_pixels[i][0]][true_pixels[i][1]] = 0
cv2.imshow("basic_edge", basic_edge)
cv2.imshow("culled_edge", culled_edge)
cv2.waitKey(0)
Source Image:
Canny Detection and Filtered (Ideal) Results:
The operation you are applying is called an area opening. I don't think there is an implementation in OpenCV, but you can find one in either scikit-image (skimage.morphology.area_opening) or DIPlib (dip.BinaryAreaOpening).
For example with DIPlib (disclosure: I'm an author) you'd amend your code as follows:
import diplib as dip
# ...
basic_edge = cv2.Canny(img_r, 240, 250)
min_threshold = 100
culled_edge = dip.BinaryAreaOpening(basic_edge > 0, min_threshold)
The output, culled_edge, is now a dip.Image object, which is compatible with NumPy arrays and you should be able to use it as such in many situations. If there's an issue, then you can cast it back to a NumPy array with culled_edge = np.array(culled_edge).
In one of my personal projects, I tried to apply the following horizontal edge mask on a grayscale image. By applying the horizontal edge mask, I am trying to detect horizontal edges in the image.
[1 2 1
0 0 0
-1 -2 -1]
When I tried to convolve my image matrix with the mask given above, the output image is rotated by 180 degrees. I am not sure whether it is expected behavior or if I am doing something wrong?
Here is code snippet of convolution.
def convolution(self):
result = np.zeros((self.mat_width, self.mat_height))
print(self.mat_width)
print(self.mat_height)
for i in range(0, self.mat_width-self.window_width):
for j in range(0, self.mat_height-self.window_height):
# deflate both mat and mask
# if j+self.window_height >= self.mat_height:
# row_index = j+self.window_height + 1
# else:
row_index = j+self.window_height
col_index = i+self.window_width
mat_masked = self.mat[j:row_index, i:col_index]
# pixel position
index_i = i + int(self.window_width / 2)
index_j = j + int(self.window_height / 2)
prod = np.sum(mat_masked*self.mask)
if prod >= 255:
result[index_i, index_j] = 255
else:
result[index_i, index_j] = 0
return result
The original grayscale input image is here -
Here is the output that is getting generated.
The indices when writing to the output are reversed. You are flipping the horizontal and vertical coordinates which would actually be transposing your image output and the output that you see is a result of transposing an image.
In addition, you aren't declaring the output size of your image correctly. The first dimension spans the rows or the height while the second dimension spans the columns or the width. The first change you must make is swapping the input dimensions of the output image:
result = np.zeros((self.mat_height, self.mat_width))
Secondly, the variable index_i is traversing horizontally while the variable index_j is traversing vertically. You just have to flip the order so that you are writing the results correctly:
if prod >= 255:
result[index_j, index_i] = 255
else:
result[index_j, index_i] = 0
If for some reason you don't want to change the order, then leave your code the way it is, including how you declared the output dimensions of your image and simply return the result transposed:
return result.T
I try to test example all black pic it not show "mostly black" i wonder please help me
from PIL import Image
im = Image.open('im.gif')
pixels = im.getdata() # get the pixels as a flattened sequence
black_thresh = 50
nblack = 0
for pixel in pixels:
if pixel < black_thresh:
nblack += 1
n = len(pixels)
if (nblack / float(n)) > 0.5:
print("mostly black")
If the image returned as tuple containing each of the color components (e.g. RGB), not a single value. You can take the average value and compare against the threshold like this:
for pixel in pixels:
if sum(pixel) / len(pixel) < black_thresh:
nblack += 1
I have a code where an image got converted to B/W.
Now I want to build a new image in reference to the original image.
The output of the original image are the X-/Y-coordinates and "1" and "0" for Black and White.
The new image will receive these information but not chronologically.
Therefore it must check and provide a negative output if it already has received information about a specific coordinate so that double entries can be avoided.
I havenĀ“t found many similar examples to this; only some examples that are going in the about direction.
Does anyone have an idea how to realize that?
UPDATE:
I built the code which converts a pixel from a white image black, if the reference pixel from the original image is black (Otherwise it leaves it white).
Furthermore the used coordinate is entered into a list and checked if used.
However, this part is not working properly.
Although the coordinate [10, 10] has been used in the loop before, the code displays Coordinate not in the system
Any help would be appreciated!
import cv2
import numpy
white = cv2.imread('white.jpg') #loading white image
white = cv2.resize(white,(640,480)) #adjusting it to the size of the original image
y = 0 #for testing purposes the white image gets blackened manually
x = 0
j = 0
while j < 50:
content = numpy.zeros((200, 2)) #creating a list with 200 entries, every entry contains 2 values
content = ([x, y]) #adding two values to the list
if condition[y, x] = 1: #condition = 1 means that in the reference picture at this coordinate the pixel is black
white[y,x] = 0 #"0" creates a black pixel at the specified coordinate on the white image
x += 5
y += 5
j += 1
x = 10 #taking a value which already has been used
y = 10
try:
b = content.index([x, y]) #check if coordinate is in the list
except ValueError:
print("Coordinate not in the system")
else:
print("Coordinate already in the system")
i = 0
while i < 100:
cv2.imshow('Bild', white) #displays the image
if cv2. waitKey(1) == ord('q'):
break
It took me a while but I was able to solve it without any complex lists or arrays.
Might not be the most elegant way but at least it is working!
I created a second white picture (=reference) which is getting compared if the coordinate has already been used or not.
If the coordinate has not been used, it will create a black pixel.
The next time it is checking this coordinate it will find a black pixel and therefore know that it has been used.
In the end the white image will contain 49 black pixels (because the position [10, 10] has already been used and will not become painted).
import cv2
import numpy
white = cv2.imread('C:\white.jpg') #loading white image
reference = cv2.imread('C:\white.jpg') #loading white image
white = cv2.resize(white,(640,480)) #adjusting it to the size of the original image
reference = cv2.resize(white,(640,480)) #adjusting it to the size of the original image
y = 0 #for testing purposes the white image gets blackened manually
x = 0
j = 0
reference[10,10] = 0
while j < 50:
if [255,255,255] in reference[y,x]:
reference[y,x] = 0 #"0" creates a black pixel at the specified coordinate on the reference image
white[y,x] = 0 #"0" creates a black pixel at the specified coordinate on the white image
print("Coordinate not in system")
else:
print("coordinate already in system")
x += 5
y += 5
j += 1
i = 0
while i < 100:
cv2.imshow('image copy', white) #displays the image
if cv2. waitKey(1) == ord('q'):
break
I'm stuck with a problem of the python wrapper for OpenCv.
I have this function that returns 1 if the number of black pixels is greater than treshold
def checkBlackPixels( img, threshold ):
width = img.width
height = img.height
nchannels = img.nChannels
step = img.widthStep
dimtot = width * height
data = img.imageData
black = 0
for i in range( 0, height ):
for j in range( 0, width ):
r = data[i*step + j*nchannels + 0]
g = data[i*step + j*nchannels + 1]
b = data[i*step + j*nchannels + 2]
if r == 0 and g == 0 and b == 0:
black = black + 1
if black >= threshold * dimtot:
return 1
else:
return 0
The loop (scan each pixel of a given image) works good when the input is an RGB
image...but if the input is a single channel image I get this error:
for j in range( width ):
TypeError: Nested sequences should have 2 or 3 dimensions
The input single channel image (called 'rg' in the next example) is taken from
an RGB image called 'src' processed with cvSplit and then cvAbsDiff
cvSplit( src, r, g, b, 'NULL' )
rg = cvCreateImage( cvGetSize(src), src.depth, 1 ) # R - G
cvAbsDiff( r, g, rg )
I've also already noticed that the problem comes from the difference image got from cvSplit...
Anyone can help me?
Thank you
widthStep and imageData are no longer valid attributes for IplImage object. Thus, the correct way to loop through each pixel and grabbing its color value would be
for i in range(0, height):
for j in range(0, width):
pixel_value = cv.Get2D(img, i, j)
# Since OpenCV loads color images in BGR, not RGB
b = pixel_value[0]
g = pixel_value[1]
r = pixel_value[2]
# cv.Set2D(result, i, j, value)
# ^ to store results of per-pixel
# operations at (i, j) in 'result' image
Hope you find this useful.
What version of OpenCV and which Python wrapper are you using? I recommend using OpenCV 2.1 or 2.2 with the Python interface that comes with the library.
I also recommend that you avoid scanning pixels manually, and instead use the low-level functions provided by OpenCV (see the Operations on Arrays part of the OpenCV docs). That way will be less error-prone and much faster.
If you want to count the number of black pixels in a single-channel image or in a color image with the COI set (so that the color image is effectively treated as a single-channel one), you could use the function CountNonZero:
def countBlackPixels(grayImg):
(w,h) = cv.GetSize(grayImg)
size = w * h
return size - cv.CountNonZero(grayImg)