def sift_train(self, images, n_clusters=120, n_jobs=-1): """ 利用SIFT,训练特征提取器(训练KMeans) 只利用SIFT得到描述子是不够的,因为每幅图片的描述子维度太高而且数量不一 致,所以利用词袋模型将这些描述子聚类得到直方图。该方法就是训练聚类器, 以便能从描述子得到最终的直方图。 Parameters ---------- images : 列表 要用来训练的图片的集合。列表中的每个图片都是二维的numpy数组(灰度 图)。 c_clusters : int 描述子聚类的类数,即特征向量的维度。 n_jobs : int 训练时用到的CPU核心数,如果是-1则使用全部核心。 """ sift = SIFT_create() descs = np.array([sift.detectAndCompute(img, None)[1] for img in images]) # Sometimes descriptor is None, turn it into np.ndarray type. descs = [d if isinstance(d, np.ndarray) else np.array([]).reshape(0, 128).astype('float32') for d in descs] # 训练好的聚类器放入self.red self.red = KMeans(n_clusters=n_clusters, n_jobs=n_jobs, random_state=42).fit(np.vstack(descs))
def sift_train(self, images, n_clusters=120, n_jobs=-1): """ 利用SIFT,训练特征提取器(训练KMeans) 只利用SIFT得到描述子是不够的,因为每幅图片的描述子维度太高而且数量不一 致,所以利用词袋模型将这些描述子聚类得到直方图。该方法就是训练聚类器, 以便能从描述子得到最终的直方图。 Parameters ---------- images : 列表 要用来训练的图片的集合。列表中的每个图片都是二维的numpy数组(灰度 图)。 c_clusters : int 描述子聚类的类数,即特征向量的维度。 n_jobs : int 训练时用到的CPU核心数,如果是-1则使用全部核心。 """ sift = SIFT_create() descs = np.array( [sift.detectAndCompute(img, None)[1] for img in images]) # Sometimes descriptor is None, turn it into np.ndarray type. descs = [ d if isinstance(d, np.ndarray) else np.array([]).reshape( 0, 128).astype('float32') for d in descs ] # 训练好的聚类器放入self.red self.red = KMeans(n_clusters=n_clusters, n_jobs=n_jobs, random_state=42).fit(np.vstack(descs))
def find_features_in_array_SIFT(self, sub_image, main_image, debug=False): # Initiate SIFT detector sift = SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(sub_image, None) kp2, des2 = sift.detectAndCompute(main_image, None) # BFMatcher with default params bf = cv2.BFMatcher() matches = bf.knnMatch(des1, des2, k=2) logging.debug("Found {} possible matches".format(len(matches))) ret_list = [] good = [] for m, n in matches: if m.distance < 0.75 * n.distance: good.append([m]) good.sort(key=lambda x: x[0].distance) if debug: # cv2.drawMatchesKnn expects list of lists as matches. img3 = cv2.drawMatchesKnn(sub_image, kp1, main_image, kp2, good, flags=2, outImg=None, matchColor=(255, 255, 0)) plt.imshow(img3), plt.show() ret_list = [] for match in good: index = match[0].trainIdx point = kp2[index].pt ret_list.append((int(point[0]), int(point[1]))) logging.debug("After filtering {}".format(len(good))) return ret_list
class SIFTDetector(Detector): def __init__(self): Detector.__init__(self) self.SIFT = SIFT_create() def __repr__(self): return "SIFTDetector(SIFT={SIFT}".format(SIFT=self.SIFT) def detect(self, image): keypoints, descriptors = self.SIFT.detectAndCompute(image.cv_image, None) return keypoints, descriptors
def getKpAndDescriptors(self, img, detector="sift"): if detector.lower() == "harris": thr = 0.01 size = 2 dst = cv2.cornerHarris(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 2, 3, 0.04) dst = cv2.dilate(dst, None) kp = np.argwhere(dst > thr * dst.max()) key_points = [cv2.KeyPoint(k[0], k[1], 2) for k in kp] sift_creator = SIFT_create() __, descriptors = sift_creator.compute(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), key_points) return descriptors[:100] elif detector.lower() == "sift": sift_creator = SIFT_create() kp, descriptors = sift_creator.detectAndCompute(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), None) return descriptors[:100]
def main(): # Parse command line and configure logging level opts = docopt.docopt(__doc__) logging.basicConfig(level=logging.WARN if opts['--quiet'] else logging.INFO) feat_detector = SIFT_create() # Determine which save function to use. save_fn, out_format = save_npz, 'npz' if opts['--lowe']: save_fn, out_format = save_lowe, 'key' im_glob = os.path.join(opts['<imgdir>'], opts['--imglob']) for im_pn in glob.glob(im_glob): logging.info('Processing %s...', im_pn) # Construct the mask and features path im_bn, im_ext = os.path.splitext(os.path.basename(im_pn)) mask_pn = os.path.join(opts['<maskdir>'], opts['--maskformat'].format( basename=im_bn, ext=im_ext )) out_pn = os.path.join(opts['<outdir>'], opts['--outformat'].format( basename=im_bn, ext=im_ext, format=out_format )) # Compute hashes of image and mask im_hash = sha256_file(im_pn) mask_hash = sha256_file(mask_pn) # Load the image and mask from disk im = np.asarray(Image.open(im_pn).convert('L')) mask = np.asarray(Image.open(mask_pn).convert('L')) # Detect keypoints and features kps, descs = feat_detector.detectAndCompute(im, mask) metadata = { 'image_path': os.path.abspath(im_pn), 'mask_path': os.path.abspath(mask_pn), 'image_sha256': im_hash, 'mask_sha256': mask_hash, } # Save output save_fn(out_pn, kps, descs, metadata)
def sift_extract(self, image): """ 利用SIFT,对给定的图片提取特征向量。使用前必须先初始化特征提取器。 Parameters ---- image : 二维numpy数组 灰度图。 Returns ------- 一维numpy数组 图片的特征向量。 """ assert self.red, "self.red should be initial!" n_clusters = self.red.n_clusters # 聚类的数量 features = np.zeros(n_clusters) # 提取到的特征 sift = SIFT_create() descriptors = sift.detectAndCompute(image, None)[1] if descriptors is None: # 如果没有找到一个描述子,就返回全是0的数组 return features y = self.red.predict(descriptors) # 对描述子聚类 features[list(set(y))] = 1 # 得到最终的特征 return features
def spoj(leva, desna, sleva=False, nove=None, orig=None): # Ukoliko nisu prosledjene tacke, moraju se naci if nove is None or orig is None \ or not nove or not orig \ or len(nove) != len(orig) \ or len(nove) < 4: # Log poruka o akciji if LOGUJ: print() print('Traže se korespondencije.') # Upotreba SIFT (scale-invariant feature transform) # algoritma za pronalazak zanimljivih tacaka na slikama sift = SIFT_create() kpl, desl = sift.detectAndCompute(leva, None) kpd, desd = sift.detectAndCompute(desna, None) # Uparivanje dobijenih deskriptora # brute-force metodom najblizih suseda parovi = BFMatcher().knnMatch(desd, desl, k=2) # Filtriranje parova izuzimanjem onih previse # dalekih; ovo nije neophodno, ali olaksava # posao RANSAC-u i znatno ga ubrzava bliski = [m for m, n in parovi if m.distance < 0.5 * n.distance] # Neophodna su barem cetiri para za # potrebe odredjivanja projekcije if len(bliski) < 4: raise ValueError # Izdvajanje originala (sa desne slike) # i slika (sa leve slike) za projekciju orig = np.float32([kpd[m.queryIdx].pt for m in bliski]).reshape(-1, 2) nove = np.float32([kpl[m.trainIdx].pt for m in bliski]).reshape(-1, 2) # Log poruka o akciji if LOGUJ: print('Uspešno odabrane korespondencije.') elif LOGUJ: print() # Log poruka o akciji if LOGUJ: print('Određuje se transformacija.') # Izracunavanje matrice projekcije M = RANSAC(nove, orig) if sleva: M = LA.inv(M) # Log poruka o akciji if LOGUJ: print('Uspešno određena transformacija.') # Dimenzije ulaznih slika dim1 = leva.shape[1], leva.shape[0] dim2 = desna.shape[1], desna.shape[0] if sleva: dim1, dim2 = dim2, dim1 # Pronalazak tacaka van slike cosk = np.array([[0, 0, 1], [dim2[0] - 1, 0, 1], [0, dim2[1] - 1, 1], [dim2[0] - 1, dim2[1] - 1, 1]]) cosk = np.array([*map(lambda x: M @ x, cosk)]) cosk = np.array([*map(lambda x: [x[0] / x[2], x[1] / x[2], 1], cosk)]) mini = cosk[:, 0].min(), cosk[:, 1].min() mini = [*map(lambda x: abs(ceil(min(x, 0))), mini)] # Nova matrica, sa dodatkom translacije koja # dosad nevidljive elemente smesta na sliku M = np.array([[1, 0, mini[0]], [0, 1, mini[1]], [0, 0, 1]]) @ M # Dimenzije slike koja nije fiksirana cosk = np.array( [*map(lambda x: [x[0] + mini[0], x[1] + mini[1], 1], cosk)]) dim = (ceil(max(cosk[:, 0].max() + 1, dim1[0] + mini[0])), ceil(max(cosk[:, 1].max() + 1, dim1[1] + mini[1]))) # Obuhvatajuci pravougaonik (bounding box) # slike koja nije fiksna zarad ustede vremena; # ukoliko su dimenzije nove slike dosta vece # od polazne, nema potrebe gledati crne piksele minx = int(ceil(cosk[:, 0].min())) maxx = int(ceil(cosk[:, 0].max())) + 1 miny = int(ceil(cosk[:, 1].min())) maxy = int(ceil(cosk[:, 1].max())) + 1 gran = (miny, minx), (maxy, maxx) # Cuvanje fiksirane i slike koju treba # transformisati pod informativnijim imenima fiksna = leva transf = desna if sleva: fiksna, transf = transf, fiksna # Log poruka o akciji if LOGUJ: print(f'Transformiše se {"leva" if sleva else "desna"} slika.') # Transformacija slike koja nije fiksirana transf = projektuj(transf, M, dim, gran) # Log poruka o akciji if LOGUJ: print('Uspešno izvršena transformacija.') # Log poruka o akciji if LOGUJ: print('Spajaju se slike.') # Uzduzne granice preklapanja if sleva: lgran = mini[0] dgran = maxx else: lgran = minx dgran = dim1[0] + mini[0] # Postavljanje fiksne slike na mesto; # prvo obrada delova pre i posle granice if sleva: transf[mini[1]:dim1[1]+mini[1], dgran :dim1[0]+mini[0]] = \ [[fiksna[i-mini[1],j-mini[0]] for j in range( dgran , dim1[0]+mini[0])] for i in range(mini[1], dim1[1]+mini[1])] else: transf[mini[1]:dim1[1]+mini[1], mini[0]: lgran ] = \ [[fiksna[i-mini[1],j-mini[0]] for j in range(mini[0], lgran )] for i in range(mini[1], dim1[1]+mini[1])] # Funkcija za filtriranje crnih piskela crn = lambda p: all(map(lambda x: x == 0, p)) # Funkcija za interpolaciju piksela duzina = dgran - lgran + 1 if sleva: pros = lambda y, x, j: (dgran - j) / duzina * x + (j - lgran + 1 ) / duzina * y else: pros = lambda x, y, j: (dgran - j) / duzina * x + (j - lgran + 1 ) / duzina * y # Tezinsko uprosecavanje (interpolacija) # necrnih piksela unutar granicnog pojasa transf[mini[1]:dim1[1] + mini[1], lgran:dgran] = [[ transf[i, j] if crn(fiksna[i - mini[1], j - mini[0]]) else fiksna[i - mini[1], j - mini[0]] if crn(transf[i, j]) else pros(fiksna[i - mini[1], j - mini[0]], transf[i, j], j) for j in range(lgran, dgran) ] for i in range(mini[1], dim1[1] + mini[1])] # Log poruka o akciji if LOGUJ: print('Uspešno spojene slike.') # Isecanje praznih ivica return iseci(transf)