#python #opencv #image-processing
#python #изображение #opencv #обработка изображений #компьютерное зрение
Вопрос:
Я работаю над проектом симулятора стрельбы, где я должен обнаруживать пулевые отверстия на изображениях. Я пытаюсь различить два изображения, чтобы я мог обнаружить новое отверстие между изображениями, но оно работает не так, как ожидалось. Между двумя изображениями есть незначительные изменения в предыдущих пулевых отверстиях из-за небольшого перемещения между кадрами камеры.
Мое первое изображение находится здесь
before.png
и второй здесь
after.png
Я попробовал этот код для проверки различий
import cv2
import numpy as np
before = cv2.imread("before.png") after = cv2.imread("after.png")
result = after - before
cv2.imwrite("result.png", result)
результат, который я получаю в result.png, — это изображение ниже
результат.png
но это не то, что я ожидал, я только хочу обнаружить новое отверстие, но оно показывает разницу с некоторыми пикселями предыдущего изображения. Результат, которого я ожидаю, таков
ожидаемый.png
Пожалуйста, помогите мне разобраться, чтобы он мог обнаруживать только большие различия.
Заранее спасибо.
Любая новая идея будет оценена по достоинству.
Ответ №1:
Чтобы найти различия между двумя изображениями, вы можете использовать индекс структурного сходства (SSIM), который был введен при оценке качества изображения: от видимости ошибок до структурного сходства. Этот метод уже реализован в библиотеке scikit-image для обработки изображений. Вы можете установить scikit-image
с pip install scikit-image
помощью .
Используя skimage.metrics.structural_similarity()
функцию из scikit-image, она возвращает score
изображение и разницу, diff
. score
Представляет собой индекс структурного сходства между двумя входными изображениями и может находиться в диапазоне [-1,1] со значениями, близкими к единице, представляющей более высокое сходство. Но поскольку вас интересует только то, где отличаются два изображения, diff
изображение, которое вы ищете. diff
Изображение содержит фактические различия в изображениях между двумя изображениями.
Далее мы находим все контуры, используя cv2.findContours()
и фильтруем для самого большого контура. Самый большой контур должен представлять новое обнаруженное различие, поскольку небольшие различия должны быть меньше, чем добавленный маркер.
Вот самая большая обнаруженная разница между двумя изображениями
Вот фактические различия между этими двумя изображениями. Обратите внимание, как были зафиксированы все различия, но поскольку новый контур, скорее всего, является самым крупным контуром, мы можем отфильтровать все другие незначительные движения между кадрами камеры.
Примечание: этот метод работает довольно хорошо, если мы предполагаем, что новый маркер будет иметь самый большой контур на diff
изображении. Если новое отверстие было меньше, вам, возможно, придется замаскировать существующие области, и любые новые контуры в новом изображении будут новым отверстием (при условии, что изображение будет однородным черным фоном с белыми отверстиями).
from skimage.metrics import structural_similarity
import cv2
# Load images
image1 = cv2.imread('1.png')
image2 = cv2.imread('2.png')
# Convert to grayscale
image1_gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
image2_gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
# Compute SSIM between the two images
(score, diff) = structural_similarity(image1_gray, image2_gray, full=True)
# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type in the range [0,1]
# so we must convert the array to 8-bit unsigned integers in the range
# [0,255] image1 we can use it with OpenCV
diff = (diff * 255).astype("uint8")
print("Image Similarity: {:.4f}%".format(score * 100))
# Threshold the difference image, followed by finding contours to
# obtain the regions of the two input images that differ
thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
contours = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
contour_sizes = [(cv2.contourArea(contour), contour) for contour in contours]
result = image2.copy()
# The largest contour should be the new detected difference
if len(contour_sizes) > 0:
largest_contour = max(contour_sizes, key=lambda x: x[0])[1]
x,y,w,h = cv2.boundingRect(largest_contour)
cv2.rectangle(result, (x, y), (x w, y h), (36,255,12), 2)
cv2.imshow('result', result)
cv2.imshow('diff', diff)
cv2.waitKey()
Вот еще один пример с разными входными изображениями. SSIM довольно хорош для обнаружения различий между изображениями
Комментарии:
1. Здравствуйте, метод работает правильно, но он слишком медленный для видео, не могли бы вы сказать мне, как сделать его быстрее?
2. При попытке повысить производительность существует множество факторов. Поскольку вы обрабатываете видео в реальном времени, один из способов — использовать многопоточность для уменьшения задержки ввода-вывода. Другой способ — уменьшить размер изображения. Но этот метод SSIM перегружает процессор, поэтому он не совсем идеален для обработки в реальном времени.
3. Для меня строка
(score, diff) =
… возвращает:(score, diff) = compare_ssim(before, after, multichannel=True) # full=True) TypeError: cannot unpack non-iterable numpy.float64 object
в чем может быть причина?4. Я считаю, что в более новых версиях функция изменилась. Я использую
opencv-contrib-python==4.2.0.32
,scikit-image==0.15.0
, иscikit-learn==0.21.3
Ответ №2:
Это мой подход: после того, как мы вычитаем одно из другого, остается некоторый шум, поэтому я просто попытался удалить этот шум. Я делю изображение на процентиль его размера и для каждого небольшого участка изображения сравниваю до и после, так что остаются только значительные фрагменты белых пикселей. Этому алгоритму не хватает точности при наличии окклюзии, то есть всякий раз, когда новый снимок перекрывает существующий.
import cv2
import numpy as np
# This is the percentage of the width/height we're gonna cut
# 0.99 < percent < 0.1
percent = 0.01
before = cv2.imread("before.png")
after = cv2.imread("after.png")
result = after - before # Here, we eliminate the biggest differences between before and after
h, w, _ = result.shape
hPercent = percent * h
wPercent = percent * w
def isBlack(crop): # Function that tells if the crop is black
mask = np.zeros(crop.shape, dtype = int)
return not (np.bitwise_or(crop, mask)).any()
for wFrom in range(0, w, int(wPercent)): # Here we are gonna remove that noise
for hFrom in range(0, h, int(hPercent)):
wTo = int(wFrom wPercent)
hTo = int(hFrom hPercent)
crop = result[wFrom:wTo,hFrom:hTo] # Crop the image
if isBlack(crop): # If it is black, there is no shot in it
continue # We dont need to continue with the algorithm
beforeCrop = before[wFrom:wTo,hFrom:hTo] # Crop the image before
if not isBlack(beforeCrop): # If the image before is not black, it means there was a hot already there
result[wFrom:wTo,hFrom:hTo] = [0, 0, 0] # So, we erase it from the result
cv2.imshow("result",result )
cv2.imshow("before", before)
cv2.imshow("after", after)
cv2.waitKey(0)
Как вы можете видеть, это сработало для предоставленного вами варианта использования. Следующий хороший шаг — сохранить массив позиций снимков, чтобы вы могли
Ответ №3:
Мой код :
from skimage.measure import compare_ssim
import argparse
import imutils
import cv2
import numpy as np
# load the two input images
imageA = cv2.imread('./Input_1.png')
cv2.imwrite("./org.jpg", imageA)
# imageA = cv2.medianBlur(imageA,29)
imageB = cv2.imread('./Input_2.png')
cv2.imwrite("./test.jpg", imageB)
# imageB = cv2.medianBlur(imageB,29)
# convert the images to grayscale
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
##########################################################################################################
difference = cv2.subtract(grayA,grayB)
result = not np.any(difference)
if result is True:
print ("Pictures are the same")
else:
cv2.imwrite("./open_cv_subtract.jpg", difference )
print ("Pictures are different, the difference is stored.")
##########################################################################################################
diff = cv2.absdiff(grayA, grayB)
cv2.imwrite("./tabsdiff.png", diff)
##########################################################################################################
grayB=cv2.resize(grayB,(grayA.shape[1],grayA.shape[0]))
(score, diff) = compare_ssim(grayA, grayB, full=True)
diff = (diff * 255).astype("uint8")
print("SSIM: {}".format(score))
#########################################################################################################
thresh = cv2.threshold(diff, 25, 255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
#s = imutils.grab_contours(cnts)
count = 0
# loop over the contours
for c in cnts:
# images differ
count=count 1
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(imageA, (x, y), (x w, y h), (0, 0, 255), 2)
cv2.rectangle(imageB, (x, y), (x w, y h), (0, 0, 255), 2)
##########################################################################################################
print (count)
cv2.imwrite("./original.jpg", imageA)
# cv2.imshow("Modified", imageB)
cv2.imwrite("./test_image.jpg", imageB)
cv2.imwrite("./compare_ssim.jpg", diff)
cv2.imwrite("./thresh.jpg", thresh)
cv2.waitKey(0)
Другой код :
import subprocess
# -fuzz 5% # ignore minor difference between two images
# -density 300
# miff:- | display
# -metric phash
# -highlight-color White # by default its RED
# -lowlight-color Black
# -compose difference # src
# -threshold 0
# -separate -evaluate-sequence Add
cmd = 'compare -highlight-color black -fuzz 5% -metric AE Input_1.png ./Input_2.png -compose src ./result.png x: '
a = subprocess.call(cmd, shell=True)
В приведенном выше коде приведены различные алгоритмы сравнения изображений для различия изображений с использованием opencv, ImageMagic, numpy, skimage и т. Д
Надеюсь, это поможет вам в полной мере.