普林斯顿算法课作业 part II 的python实现(二)Seam-carving

Seam-carving

  • 问题
  • 思路
  • 代码
  • 结果
  • 总结

问题

原问题网页:Seam-carving
问题大致描述如下:接缝裁剪(Seam-carving)是一种图像处理的方法。它不是简单地将图片进行等比例缩放,而是在缩放的同时,尽可能地保留了原图片的最主要的特征。换句话说,它是将原图片中一些无关紧要的地方给丢弃了。而这次作业就是要实现这个功能。
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第1张图片
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第2张图片

关于接缝裁剪(Seam-carving)的更多信息,可以参见 视频(需FQ)

思路

这是一个图像处理的问题,python里面有几个常用的图像处理的库,比如pillow、OpenCV。这里利用 pillow 库来进行处理。关于 pillow 库的安装和教程参见官方文档。利用 pillow 库,可以很方便地读取图片的像素信息,获得一个像素矩阵,矩阵中的每一个元素表示了该像素上的颜色信息(RGB格式)

要解决原问题,可以按如下几个步骤进行:

  1. 计算能量(Energy calculation)
    用能量来定义每一个像素的重要程度。这里采用双梯度能量函数,详见原网页。
  2. 定义接缝(Seam identification)
    将每个元素与它下面及其两边的元素(如果有的话)相连,构成一个无圈有向图(DAG)。接缝定义如下:从顶部像素到底部像素能量之和最短的路径。
  3. 移除接缝(Seam removal)
    移除接缝上的所有像素

代码

# -*- coding: utf-8 -*-
"""
Created on Sat Jan  2 15:30:45 2021

@author: zxw
"""
# 设置文件路径
import os
os.chdir('C:/Users/zxw/Desktop/修身/与自己/数据科学/算法/seam')
import networkx as nx
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

class SeamCarver:
    def __init__(self,picture):
        self.im = Image.open(picture) #读取图片
        self.W = self.im.size[0] #宽度
        self.H = self.im.size[1] #高度
        self.matrix = np.array(Image.open(picture)) #用一个三维矩阵存储像素信息
        self.matrix = self.matrix.astype(int)
        self.energy_matrix = np.arange(self.H*self.W).reshape(self.H,self.W)
        self.energy_matrix = self.energy_matrix.astype(float)
        for i in range(self.H):
            for j in range(self.W):
                Left = (j == 0)
                Right = (j == self.W - 1)
                Top = (i == 0)
                Bottom = (i == self.H-1)
                if (Left)|(Right)|(Top)|(Bottom):
                    self.energy_matrix[i][j] = 1000
                else:
                    dx = self.matrix[i+1][j] - self.matrix[i-1][j]
                    dy = self.matrix[i][j+1] - self.matrix[i][j-1]
                    dx_square = sum(np.square(dx))
                    dy_square = sum(np.square(dy))
                    self.energy_matrix[i][j] =  np.sqrt(dx_square+dy_square)
    #展示图片
    def picture(self):
        plt.imshow(self.matrix)
    #定义能量
    def energy(self,x,y):
        return self.energy_matrix[y][x]
    #定义横向接缝
    #利用矩阵的转置
    def findHorizontalSeam(self):
        t = self.W
        self.W = self.H
        self.H = t
        self.energy_matrix = self.energy_matrix.T
        H_Seam = self.findVerticalSeam()
        self.energy_matrix = self.energy_matrix.T
        t = self.W
        self.W = self.H
        self.H = t
        return H_Seam 
    #定义纵向接缝
    def findVerticalSeam(self):
        graph = nx.DiGraph()
        graph.add_nodes_from([i for i in range(self.W*self.H+2)])
        a = self.W
        b = self.H
        for i in range(a):
            graph.add_edge(0, i+1, weight = 1000)
            graph.add_edge(a*b - i, a*b+1, weight = 0)
        for k in range(a*(b-1)):
            i = k % a
            j = int((k/a))
            if i == 0:
                graph.add_edge(k+1,k+1+a, weight = self.energy(i, j+1))
                graph.add_edge(k+1,k+2+a, weight = self.energy(i+1, j+1))
            elif i == a-1:
                graph.add_edge(k+1,k+a, weight = self.energy(i-1, j+1))
                graph.add_edge(k+1,k+1+a, weight = self.energy(i, j+1))
            else:
                graph.add_edge(k+1,k+a, weight = self.energy(i-1, j+1))
                graph.add_edge(k+1,k+1+a, weight = self.energy(i, j+1))
                graph.add_edge(k+1,k+2+a, weight = self.energy(i+1, j+1))
        Nodes = nx.shortest_path(G=graph,source=0,target=b*a+1,weight='weight')[1:-1]
        V_Seam = []
        for i in range(len(Nodes)):
            V_Seam.append((Nodes[i] % a)-1)
        return V_Seam
    def removeHorizontalSeam(self,seam):
        New_matrix = np.zeros((self.H-1,self.W,3))
        for i in range(self.W):
            k = 0
            for j in range(self.H):
                if j != seam[i]:
                    New_matrix[k][i] = self.matrix[j][i]
                    k = k+1
        New_matrix = New_matrix.astype(int)
        self.matrix = New_matrix
        New_energy_matrix = np.arange(self.H*(self.W-1)).reshape(self.H,self.W-1)
        New_energy_matrix = New_energy_matrix.astype(float)
        for i in range(self.W):
            k = 0
            for j in range(self.H):
                if j != seam[i]:
                    New_energy_matrix[k][i] = self.energy_matrix[j][i]
                    k = k+1
        self.energy_matrix = New_energy_matrix
        self.H = self.H-1        
    def removeVerticalSeam(self,seam):
        New_matrix = np.zeros((self.H,self.W-1,3))
        for i in range(self.H):
            k = 0
            for j in range(self.W):
                if j != seam[i]:
                    New_matrix[i][k] = self.matrix[i][j]
                    k = k+1
        New_matrix = New_matrix.astype(int)
        self.matrix = New_matrix
        New_energy_matrix = np.arange(self.H*(self.W-1)).reshape(self.H,self.W-1)
        New_energy_matrix = New_energy_matrix.astype(float)
        for i in range(self.H):
            k = 0
            for j in range(self.W):
                if j != seam[i]:
                    New_energy_matrix[i][k] = self.energy_matrix[i][j]
                    k = k+1
        self.energy_matrix = New_energy_matrix
        self.W = self.W-1
# 测试
test= SeamCarver("HJocean.png")
test.picture()
for i in range(200):
    test.removeVerticalSeam(test.findVerticalSeam())
    print(i)
test.picture()

结果

普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第3张图片

普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第4张图片
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第5张图片
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第6张图片

普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第7张图片普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第8张图片
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第9张图片
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第10张图片
普林斯顿算法课作业 part II 的python实现(二)Seam-carving_第11张图片

总结

这次作业主要涉及和实现了以下功能:

  1. pillow库的使用:读取图片,获得像素矩阵
  2. 利用 networkx 计算加权有向图最短路
  3. 有向图的可视化

缺点是运行速度较慢

你可能感兴趣的:(算法,算法,python,图论)