草庐IT

【OpenCV图像处理1】图像&视频的加载与显示

LeoATLiang 2023-10-21 原文

文章目录

一、图像&视频的加载与显示

1、创建和显示窗口

1.1 namedWindow() 创建命名窗口

namedWindow()用法:

cv2.namedWindow(winname, flags)

参数说明:

  • winname:新建窗口的名称,可任意取(字符串类型)。

  • flags:窗口的标识,一般默认为 WINDOW_AUTOSIZE ,还有其他几种参数,具体用法如下表所示:

参数描述
WINDOW_NORMAL显示图像后,允许用户随意调整窗口大小
WINDOW_AUTOSIZE根据图像大小显示窗口,不允许用户调整大小
WINDOW_OPENGL创建窗口的时候会支持OpenGL
WINDOW_FULLSCREEN全屏显示窗口
WINDOW_FREERATIO调整图像尺寸以充满窗口
WINDOW_KEEPRATIO保持图像的比例
WINDOW_GUI_EXPANDED创建的窗口允许添加工具栏和状态栏
WINDOW_GUI_NORMAL创建没有状态栏和工具栏的窗口

以上两种标蓝色的参数是最为常见的。

1.2 resizeWindow() 修改窗口尺寸

resizeWindow()用法:

cv2.resizeWindow(winname, width, height)

参数说明:

  • winname:被修改窗口的名称。
  • width 和 height:窗口的宽和高。

注意:当 namedWindow(winname, flags) 中的 flages 设为 WINDOW_AUTOSIZE 时,将不会对窗口尺寸进行修改。

1.3 imshow() 显示窗口

imshow()用法:

cv2.imshow(winname, mat)

参数说明:

  • winname:窗口名称,它是一个字符串。
  • mat:要展现的对象。

若 mat 设置为0,即不展示任何对象,只是一个黑框。

1.4 waitKey() 等待用户输入

waitKey()用法:

cv2.waitKey(delay)

参数说明:

  • delay:以毫秒为单位,该函数等待任何键盘事件指定的毫秒。如果在这段时间内按下任何键,程序将继续运行。
  • 如果 0 被传递,它将无限期地等待一次敲击键。

当然也可以设置按指定键退出,即使用 if 判断语句:

key = cv2.waitKey(0)
# 按q键退出
if key & 0xFF == ord('q'):
	break

1.5 destroyWindow()、destroyAllWindows销毁窗口

1、destroyWindow()用法:

cv2.destroyWindow(winname)

参数说明:

  • winname:需要销毁的窗口名称。

2、destroyAllWindows用法:

cv2.destroyAllWindows()

参数说明:

  • 无参数

3、两者区别:

destroyAllWindows() 会销毁我们创建的所有窗口

如果要销毁任何特定的窗口,使用函数 destroyWindow() ,并在其中传递确切的窗口名称作为参数。

1.6 代码实现

import cv2

cv2.namedWindow('windows', cv2.WINDOW_NORMAL)  # 创建命名窗口
cv2.resizeWindow('windows', 640, 360)  # 修改窗口尺寸

cv2.imshow('windows', 0)  # 显示窗口
cv2.waitKey(0)  # 等待用户输入
cv2.destroyAllWindows()  # 销毁窗口

2、加载显示图像

2.1 imread() 读取图像

imread()用法:

cv2.imread(filename, flags)

参数说明:

  • filename:要加载的文件名(路径)。
  • flags:读取图像的方式,一般默认为 IMREAD_COLOR ,还有其他几种参数,具体用法如下表所示:
参数描述
IMREAD_UNCHANGED如果设置,则按原样返回加载的图像(带有Alpha通道,否则将被裁剪)。
IMREAD_GRAYSCALE如果设置,请始终将图像转换为单通道灰度图像。
IMREAD_COLOR如果设置,请始终将图像转换为3通道BGR彩色图像。
IMREAD_ANYDEPTH如果设置,则当输入具有相应的深度时返回16位/ 32位图像,否则将其转换为8位。
IMREAD_ANYCOLOR如果设置,将以任何可能的颜色格式读取图像。
IMREAD_LOAD_GDAL如果设置,请使用Gdal驱动程序加载图像。
IMREAD_REDUCED_GRAYSCALE_2如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸减小1/2。
IMREAD_REDUCED_COLOR_2如果设置,请始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/2。
IMREAD_REDUCED_GRAYSCALE_4如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸减小1/4。
IMREAD_REDUCED_COLOR_4如果设置,请始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/4。
IMREAD_REDUCED_GRAYSCALE_8如果设置,请始终将图像转换为单通道灰度图像,并且图像尺寸减小1/8。
IMREAD_REDUCED_COLOR_8如果设置,请始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/8。
IMREAD_IGNORE_ORIENTATION如果设置,请不要根据EXIF的方向标志旋转图像。

以上三种标蓝色的参数是最为常见的,除了这三个标志,我们可以分别简单地传递整数-1、0或1。

2.2 代码实现

import cv2

cv2.namedWindow('window', cv2.WINDOW_NORMAL)  # 创建命名窗口
cv2.resizeWindow('window', 502, 345)  # 修改窗口尺寸

cat = cv2.imread('../resource/cat.jpg', cv2.IMREAD_GRAYSCALE)  # 读取图片

cv2.imshow('window', cat)  # 显示窗口(图片)
cv2.waitKey(0)  # 等待用户输入
cv2.destroyAllWindows()  # 销毁窗口
  • 如果以 IMREAD_GRAYSCALE0 的方式读取图像,那么将呈现灰色图像:

  • 如果以 IMREAD_COLOR1 的方式读取图像,那么将呈现彩色或原有图像:

  • 如果 imread() 未指定 flag ,那么它默认以彩色的方式来读取图像。

3、保存图像

3.1 imwrite() 保存图像

imwrite()用法:

cv2.imwrite(filename, img)

参数说明:

  • filename:要保存的文件名(路径)。
  • img:要保存的图像。cv2.imwrite('../resource/cat.png', cat) ,这会将图像以PNG格式保存在工作目录中。

3.2 代码实现

在下面的程序中,以灰度加载图像,显示图像,按 q 键直接退出而不保存,或者按 s 键保存图像并退出:

import cv2

cv2.namedWindow('window', cv2.WINDOW_NORMAL)  # 创建命名窗口
cv2.resizeWindow('window', 502, 345)  # 修改窗口尺寸

cat = cv2.imread('../resource/cat.jpg', cv2.IMREAD_GRAYSCALE)  # 读取图片
cv2.imshow('window', cat)  # 显示窗口(图片)
key = cv2.waitKey(0)  # 等待用户输入

if key & 0xFF == ord('q'):  # 按 q 退出
    cv2.destroyAllWindows()
elif key & 0xFF == ord('s'):  # 按 s 保存和退出
    cv2.imwrite('../resource/cat.png', cat)
    cv2.destroyAllWindows()

如果使用的是64位计算机,则必须按如示修改行: key & 0xFF == ord(‘q’)

3.3 ord() 函数

我们想一想:cv2.waitKey() 返回的是什么数据类型?

key = cv2.waitKey(0)
print(type(key))print(type(cv2.waitKey(0)))
<class 'int'>

cv2.waitKey() 返回的是整型,那么我们如果写成 if key & 0xFF == 'q':行不行?

答案当然是不行,因为我们知道 key 是整型,而 ‘q’ 是字符串类型,两者属于不同类型,不能给直接作比较,这就引出了 ord() 函数

ord() 函数的作用是:将字符串转换为ASCII,也就是将字符串类型转换为相对应的整型,然后就可以直接作比较。

3.4 修改 bug

当上述代码运行时发现:我们不仅可以按 q 键退出还可以按其他键退出,这就不满足我们的要求,但我们可以添加一个 while 循环:

import cv2

cv2.namedWindow('window', cv2.WINDOW_NORMAL)  # 创建命名窗口
cv2.resizeWindow('window', 502, 345)  # 修改窗口尺寸

cat = cv2.imread('../resource/cat.jpg', cv2.IMREAD_GRAYSCALE)  # 读取图片

while True:
    cv2.imshow('window', cat)  # 显示窗口(图片)
    key = cv2.waitKey(0)  # 等待用户输入

    if key & 0xFF == ord('q'):  # 按 q 退出
        break
    elif key & 0xFF == ord('s'):  # 按 s 保存和退出
        cv2.imwrite('../resource/cat.png', cat)
    else:
        print(key)  # 打印 key 值

以上代码实现:按 q 键退出,按 s 键保存图像,按 其他 键打印该键的ASCII码。

4、视频采集

4.1 VideoCapture() 获取视频设备

VideoCapture() 用法:

cap = cv2.VideoCapture(0)

参数说明:

  • 设备索引
    • 0 表示打开笔记本的内置摄像头,1 则打开外置摄像头。
  • 视频文件
    • 视频文件所在的路径。(以下会提及)

4.2 cap.read() 读取视频帧

cap.read()用法

ret, frame = cap.read()

返回值说明:

  • ret:返回布尔值(True / False ),代表是否读取到图像。
  • frame:表示截取的一帧图像。

4.3 cap.release() 释放捕获

cap.release()用法

cap.release()

4.4 代码实现

import cv2

cv2.namedWindow('video', cv2.WINDOW_NORMAL)  # 创建窗口
cv2.resizeWindow('video', 640, 480)  # 修改窗口尺寸

cap = cv2.VideoCapture(0)  # 获取视频设备

while True:
    # 从摄像头读视频帧
    ret, frame = cap.read()
    # 将视频帧在窗口显示
    cv2.imshow('video', frame)
    # 等待键盘事件,如果为q,退出
    key = cv2.waitKey(1)
    if key & 0xFF == ord('q'):
        break

cap.release()  # 释放VideoCapture
cv2.destroyAllWindows()  # 销毁窗口

5、读取视频文件

5.1 VideoCapture() 读取视频

VideoCapture() 用法:

cap = cv2.VideoCapture('../resource/fire.mp4')

参数说明:

  • 视频文件所在的路径。

5.2 VideoCapture() 小结

参数为设备索引时:获取视频设备;参数为视频文件时:从视频文件中读取视频帧。

5.3 代码实现

import cv2

cv2.namedWindow('video', cv2.WINDOW_NORMAL)  # 创建窗口
cv2.resizeWindow('video', 640, 360)  # 修改窗口尺寸

cap = cv2.VideoCapture('../resource/fire.mp4')  # 从视频文件读取视频帧

while True:
    # 从摄像头读视频帧
    ret, frame = cap.read()
    # 将视频帧在窗口显示
    cv2.imshow('video', frame)
    # 等待键盘事件,如果为q,退出
    key = cv2.waitKey(1000 // 30)
    if key & 0xFF == ord('q'):
        break

cap.release()  # 释放VideoCapture
cv2.destroyAllWindows()  # 销毁窗口

6、录制并保存视频

6.1 VideoWriter_fourcc() 多媒体格式

VideoWriter_fourcc()用法:

fourcc = cv2.VideoWriter_fourcc(c1, c2, c3, c4)

参数说明:

  • Fedora 中:DIVX,XVID,MJPG,X264,WMV1,WMV2。(最好使用XVID。MJPG会生成大尺寸的视频。X264会生成非常小的尺寸的视频)
  • Windows 中:DIVX(.avi 尚待测试和添加)
  • OSX 中:MJPG(.mp4),DIVX(.avi),X264(.mkv)。

cv2.VideoWriter_fourcc() 的参数可以写成 ('M','J','P','G') 或者 (*'MJPG') 传递。

6.2 视频录制 VideoWriter()

VideoWriter()用法:

vw = cv2.VideoWriter('../resource/out.avi', fourcc, 25, (640, 480))

参数说明:

  • 输出文件
  • 多媒体文件格式 VideoWriter_fourcc()
  • 帧率
  • 分辨率大小
  • 颜色标志(默认为True ),编码器期望颜色帧,否则它与灰度帧一起工作。

6.3 写数据 vw.write

vw.write用法:

vw.write(frame)

参数说明:

  • ret, frame = cap.read() 将读取到的视频帧,再通过 vw.write(frame) 将数据写入多媒体文件。

6.4 释放资源 vw.release

vw.release用法:

vw.release()

完成工作后释放所有内容(释放VideoCapture、释放VideoWriter、销毁窗口)。

6.5 代码实现

import cv2

# 创建VideoWriter为写多媒体文件
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
vw = cv2.VideoWriter('../resource/out.avi', fourcc, 25, (640, 480))

cv2.namedWindow('video', cv2.WINDOW_NORMAL)  # 创建窗口
cv2.resizeWindow('video', 640, 360)  # 修改窗口尺寸

cap = cv2.VideoCapture(0)  # 获取视频设备

while True:
    # 从摄像头读视频帧
    ret, frame = cap.read()
    # 将视频帧在窗口显示
    cv2.imshow('video', frame)
    
    # 写数据到多媒体文件
    vw.write(frame)

    # 等待键盘事件,如果为q,退出
    key = cv2.waitKey(1000 // 30)
    if key & 0xFF == ord('q'):
        break

cap.release()  # 释放VideoCapture
vw.release()  # 释放VideoWriter
cv2.destroyAllWindows()  # 销毁窗口

6.6 代码优化

1、使用 isOpened() 判断摄像头是否已打开?

while cap.isOpened():

判断摄像头是否为打开状态

2、采集数据时要判断数据是否获取到了?

if ret == True:
	...
else:
	break

3、优化后代码

import cv2

# 创建VideoWriter为写多媒体文件
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
vw = cv2.VideoWriter('../resource/out.avi', fourcc, 25, (640, 480))

cv2.namedWindow('video', cv2.WINDOW_NORMAL)  # 创建窗口
cv2.resizeWindow('video', 640, 360)  # 修改窗口尺寸

cap = cv2.VideoCapture(0)  # 获取视频设备

# 判断摄像头是否为打开状态
while cap.isOpened():
    # 从摄像头读视频帧
    ret, frame = cap.read()

    if ret == True:
        # 将视频帧在窗口显示
        cv2.imshow('video', frame)
        # 写数据到多媒体文件
        vw.write(frame)

        # 等待键盘事件,如果为q,退出
        key = cv2.waitKey(1000 // 30)
        if key & 0xFF == ord('q'):
            break
    else:
        break

cap.release()  # 释放VideoCapture
vw.release()  # 释放VideoWriter
cv2.destroyAllWindows()  # 销毁窗口

7、鼠标控制

7.1 设置鼠标回调 setMouseCallback()

setMouseCallback()用法:

cv2.setMouseCallback(winname, callback, userdata)

参数说明:

  • winname:窗口名称
  • callback:鼠标事件的回调函数
  • userdata:传递给回调函数的可选参数(默认为None)

7.2 回调函数 callback()

callback()用法:

callback(event, x, y, flags, userdata)

参数说明:

  • event:鼠标事件(鼠标移动、左键、右键…)

  • x, y:鼠标所在坐标点

  • flags:鼠标键及组合键

  • userdata:传递给回调函数的可选参数(默认为None)

event类型如下表所示:

event描述
EVENT_MOUSEMOVE0鼠标移动
EVENT_LBUTTONDOWN1按下鼠标左键
EVENT_RBUTTONDOWN2按下鼠标右键
EVENT_MBUTTONDOWN3按下鼠标中键
EVENT_LBUTTONUP4左键释放
EVENT_RBUTTONUP5右键释放
EVENT_MBUTTONUP6中键释放
EVENT_LBUTTONDBLCLK7左键双击
EVENT_RBUTTONDBLCLK8右键双击
EVENT_MBUTTONDBLCLK9中键双击
EVENT_MOUSEWHEEL10鼠标滚轮上下移动
EVENT_MOUSEHWHEEL11鼠标滚轮左右移动

flags类型如下表所示:

flags描述
EVENT_FLAG_LBUTTON1按下左键
EVENT_FLAG_RBUTTON2按下右键
EVENT_FLAG_MBUTTON4按下中键
EVENT_FLAG_CRTLKEY8按下CTRL键
EVENT_FLAG_SHIFTKEY16按下SHIFT键
EVENT_FLAG_ALTKEY32按下ALT键

7.3 设置背景 np.zeros()

np.zeros()用法:

img = np.zeros(shape, dtype)

参数说明:

  • shape:创建新数组的形状(维度),与显示窗口相反。
  • dtype:数据类型

7.4 代码实现

import cv2
import numpy as np

# 设置鼠标回调函数
def callback(event, x, y, flags, userdata):
    print(event, x, y, flags, userdata)

# 创建、修改窗口
cv2.namedWindow('mouse_callback', cv2.WINDOW_NORMAL)
cv2.resizeWindow('mouse_callback', 640, 360)

# 设置鼠标回调
cv2.setMouseCallback('mouse_callback', callback, 'userdata')
# 显示窗口和背景
img = np.zeros((360, 640, 3), np.uint8)

while True:
    cv2.imshow('mouse_callback', img)
    key = cv2.waitKey(0)
    if key & 0xFF == ord('q'):
        break

# 销毁窗口
cv2.destroyAllWindows()

8、TrackBar控件

8.1 创建TrackBar控件 createTrackbar()

createTrackbar()用法:

cv2.createTrackbar(trackbarName, windowName, value, count, onChange)

参数说明:

  • trackbarName:轨迹条名
  • windowName:窗口名
  • value:滑块初始位置
  • count:滑块达到最大位置的值(最小值为0,最大值为count)
  • onChange:默认值为0,指回调函数
  • userdate:默认值为0,用户传递给回调函数的数据值

8.2 获取当前TrackBar值 getTrackbarPos()

getTrackbarPos()用法:

cv2.getTrackbarPos(trackbarName, windowName)

参数说明:

  • trackbarName:轨迹条名
  • windowName:窗口名

8.3 代码实现

import cv2
import numpy as np

# 定义回调函数
def callback(value):
    print(value)

# 创建窗口
cv2.namedWindow('TrackBar', cv2.WINDOW_NORMAL)

# 创建trackbar
cv2.createTrackbar('R', 'TrackBar', 0, 255, callback)
cv2.createTrackbar('G', 'TrackBar', 0, 255, callback)
cv2.createTrackbar('B', 'TrackBar', 0, 255, callback)

# 设置纯黑色背景图片(640 * 480)
img = np.zeros((480, 640, 3), np.uint8)

while True:

    # 获取当前TrackBar值
    r = cv2.getTrackbarPos('R', 'TrackBar')
    g = cv2.getTrackbarPos('G', 'TrackBar')
    b = cv2.getTrackbarPos('B', 'TrackBar')

    # 改变背景图片颜色
    img[:] = [b, g, r]

    # 显示
    cv2.imshow('TrackBar', img)

    key = cv2.waitKey(10)
    if key & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()

有关【OpenCV图像处理1】图像&视频的加载与显示的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  7. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  8. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  9. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  10. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

随机推荐