美豬供應量超過需求 豬隻恐被安樂死_潭子電動車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

摘錄自2020年4月21日自由時報報導

美國各地學校和餐廳因武漢肺炎疫情紛紛關閉,而這兩者可以說是豬肉的最大消費者,因此連帶地讓豬肉供應超過需求,目前每在市場上賣出一頭豬可能會賠37美元(約新台幣1112元),在這種情況下豬隻也許會被安樂死。

據《福斯新聞》報導,美國目前約有6萬名養豬戶,愛荷華州豬肉生產協會會長帕斯蒂安(Mike Paustian)指出,現在豬價如此低的原因之一,在於食品和消費者之間的聯繫受到了干擾,另一個迫在眉睫的問題是,隨著賣不出去豬越長越大,已超過了加工廠的標準。

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

美國農業部(USDA)對此已宣布了武漢肺炎紓困計劃,其中包括計劃購買30億美元(約新台幣900億元)農產品和向養豬戶直接支付16億美元(約新台幣480億元)。

國際新聞
美國
家豬
安樂死
經濟動物
動物福利

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

字符串太占內存了,我想了各種奇思淫巧對它進行壓縮_潭子電動車

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

一:背景

1. 講故事

在我們的一個全內存項目中,需要將一家大品牌店鋪小千萬的trade灌入到內存中,大家知道trade中一般會有訂單來源,省市區 ,當把這些字段灌進去后,你會發現他們特別侵蝕內存,因為都是字符串類型,不知道大家對內存侵蝕性是不是很清楚,我就問一個問題。

Question: 一個空字符串佔用多大內存? 你知道嗎?

思考之後,下面我們就一起驗證下,使用windbg去託管堆一查究竟,代碼如下:


        static void Main(string[] args)
        {
            string s = string.Empty;

            Console.ReadLine();
        }

0:000> !clrstack -l
OS Thread Id: 0x308c (0)
        Child SP               IP Call Site
ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 19]
    LOCALS:
        0x00000087391febd8 = 0x000002605da91420
0:000> !DumpObj /d 000002605da91420
Name:        System.String
String:      
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9eb2b85a0  4000281        8         System.Int32  1 instance                0 m_stringLength
00007ff9eb2b6838  4000282        c          System.Char  1 instance                0 m_firstChar
00007ff9eb2b59c0  4000286       d8        System.String  0   shared           static Empty
                                 >> Domain:Value  000002605beb2230:NotInit  <<
0:000> !objsize 000002605da91420
sizeof(000002605da91420) = 32 (0x20) bytes (System.String)

從圖中你可以看到,僅僅一個空字符串就要佔用 32byte,如果500w個空字符串就是: 32byte x 500w = 152M,是不是不算不知道,一算嚇一跳。。。 這還僅僅是一個什麼都沒有的空字符串哦。

2. 回歸到Trade

問題也已經擺出來了,接下來回歸到Trade中,為了方便演示,先模擬以文件的形式從數據庫讀取20w的trade。

    class Program
    {
        static void Main(string[] args)
        {
            var trades = Enumerable.Range(0, 20 * 10000).Select(m => new Trade()
            {
                TradeID = m,
                TradeFrom = File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
                                 .ElementAt(m % 4)
            }).ToList();

            GC.Collect();  //方便測試,把臨時變量清掉
            Console.WriteLine("執行成功");
            Console.ReadLine();
        }
    }

    class Trade
    {
        public int TradeID { get; set; }
        public string TradeFrom { get; set; }
    }

然後用windbg去跑一下託管堆,再量一下trades的大小。


0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
00007ff9eb2b59c0   200200      7010246 System.String

0:000> !objsize 0x000001a5860629a8
sizeof(000001a5860629a8) = 16097216 (0xf59fc0) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

從上面輸出中可以看到託管堆有200200 = 20w(程序分配)+ 200(系統分配)個,然後再看size: 16097216/1024/1024= 15.35M,這就是展示的所有原始情況。

二:壓縮技巧分析

1. 使用字典化處理

其實在託管堆上有20w個字符串,但你仔細觀察一下會發現其實就是4種狀態的重複显示,要麼一淘,要麼淘寶。。。這就給了我優化機會,何不在獲取數據的時候構建好OrderFrom的字典,然後在trade中附增一個TradeFromID記錄字典中的映射值,因為特徵值少,用byte就可以了,有了這個思想,可以把代碼修改如下:

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污


    class Program
    {
        public static Dictionary<int, string> orderfromDict = new Dictionary<int, string>();

        static void Main(string[] args)
        {
            var trades = Enumerable.Range(0, 20 * 10000).Select(m =>
            {
                var tradefrom = File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
                                 .ElementAt(m % 4);

                var kv = orderfromDict.FirstOrDefault(k => k.Value == tradefrom);

                if (kv.Key == 0)
                {
                    orderfromDict.Add(orderfromDict.Count + 1, tradefrom);
                }

                var trade = new Trade() { TradeID = m, TradeFromID = (byte)kv.Key };

                return trade;

            }).ToList();

            GC.Collect();  //方便測試,把臨時變量清掉

            Console.WriteLine("執行成功");

            Console.ReadLine();
        }
    }

    class Trade
    {
        public int TradeID { get; set; }

        public byte TradeFromID { get; set; }

        public string TradeFrom
        {
            get
            {
                return Program.orderfromDict[TradeFromID];
            }
        }
    }

代碼還是很簡單的,接下來用windbg看一下空間到底壓縮了多少?

0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
00007ff9eb2b59c0      204        10386 System.String

0:000> !clrstack -l
OS Thread Id: 0x2ce4 (0)
        Child SP               IP Call Site
ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 42]
    LOCALS:
        0x0000006f4d9ff078 = 0x0000016fdcf82ab8

0000006f4d9ff288 00007ff9ecd96c93 [GCFrame: 0000006f4d9ff288] 
0:000> !objsize 0x0000016fdcf82ab8
sizeof(0000016fdcf82ab8) = 6897216 (0x693e40) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

從上面的輸出中可以看到,託管堆上string現在是:204 = 4(程序分配) + 200(系統分配)個,這4個就是字典中的4個哦,空間的話:6897216 /1024/1024= 6.57M,對應之前的 15.35M優化了將近60%。

雖然優化了60%,但這種優化是破壞性的優化,需要修改我的Trade結構,同時還要定義個Dictionary,而且還有不小幅度的修改業務邏輯,大家都知道線上的代碼是能不改則不改,不改肯定沒錯,改出問題肯定是你兜着走,是吧,那問題就來了,如何最小化的修改而且還能壓縮空間,有這樣兩全其美的事情嗎???

2. 利用字符串駐留池

貌似一說出來,大家都如夢初醒,駐留池的出現就是為了解決這個問題,CLR會在內部維護了一個我剛才定義的字典機制,重複的字符串就不需要在堆上再次分配,直接存它的引用地址即可,如果你不清楚駐留池,建議看一下我這篇: https://www.cnblogs.com/huangxincheng/p/12799736.html

接下來只需要在tradefrom 字段包一層 string.Intern 即可,改動不要太小,代碼如下:


        static void Main(string[] args)
        {
            var trades = Enumerable.Range(0, 20 * 10000).Select(m => new Trade()
            {
                TradeID = m,
                TradeFrom = string.Intern(File.ReadLines(Environment.CurrentDirectory + "//orderfrom.txt")
                                 .ElementAt(m % 4)),   //包一層 string.Intern
            }).ToList();

            GC.Collect();  //方便測試,把臨時變量清掉
            Console.WriteLine("執行成功");
            Console.ReadLine();
        }

然後用windbg抓一下託管堆。


0:000> !dumpheap -stat 
Statistics:
              MT    Count    TotalSize Class Name
00007ff9eb2b59c0      204        10386 System.String

0:000> !clrstack -l
OS Thread Id: 0x13f0 (0)
        Child SP               IP Call Site

ConsoleApp6.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp6\Program.cs @ 27]
    LOCALS:
        0x0000005e4d3ff0a8 = 0x000001f8a15129a8

0000005e4d3ff2b8 00007ff9ecd96c93 [GCFrame: 0000005e4d3ff2b8] 
0:000> !objsize 0x000001f8a15129a8
sizeof(000001f8a15129a8) = 8497368 (0x81a8d8) bytes (System.Collections.Generic.List`1[[ConsoleApp6.Trade, ConsoleApp6]])

觀察后發現,當用了駐留池之後空間為: 8497368 /1024/1024 =8.1M,你可能有疑問,為什麼和字典化相比內存要大24%呢? 仔細觀察你會發現,當用駐留池后,List<Trade> 中的TradeFrom存的是string在堆中的內存地址,在x64機器上要佔用8個字節,而字典化方式內存堆上Trade是不分配TradeFrom,而是用了一個byte來替代,總體來說相當於一個trade省了7byte的空間,然後用windbg看一下。


0:000> !da -length 1 -details 000001f8b16f9b68
Name:        ConsoleApp6.Trade[]
Size:        2097176(0x200018) bytes
Array:       Rank 1, Number of elements 262144, Type CLASS

    Fields:
                      MT    Field   Offset                 Type VT     Attr            Value Name
        00007ff9eb2b85a0  4000001       10             System.Int32      1     instance                    0     <TradeID>k__BackingField
        00007ff9eb2b59c0  4000002        8            System.String      0     instance     000001f8a1516030     <TradeFrom>k__BackingField

0:000> !DumpObj /d 000001f8a1516030
Name:        System.String
String:      WAP

可以看到, 000001f8a1516030 就是 堆上 string=Wap的引用地址,這個地址佔用了8byte空間。

再回頭dump一下使用字典化方式的Trade,可以看到它是沒有 <TradeFrom>k__BackingField 字段的。


0:000> !da -length 1 -details 000001ed52759ac0
Name:        ConsoleApp6.Trade[]
Size:        262168(0x40018) bytes
Array:       Rank 1, Number of elements 32768, Type CLASS
    Fields:
                      MT    Field   Offset                 Type VT     Attr            Value Name
        00007ff9eb2b85a0  4000002        8             System.Int32      1     instance                    0     <TradeID>k__BackingField
        00007ff9eb2b7d20  4000003        c              System.Byte      1     instance                    0     <TradeFromID>k__BackingField


三:總結

大家可以根據自己的情況使用,使用駐留池方式是改變最小的,簡單粗暴,自己構建字典化雖然最省內存,但需要修正業務邏輯,這個風險自擔哦。。。

如您有更多問題與我互動,掃描下方進來吧~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

顯卡持續缺貨,中國加密貨幣礦工現在看上 RTX 3060 遊戲筆電, 挖礦效率意外的高_潭子電動車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

加密貨幣價格 2017 年崩盤之後,過去三年挖礦熱潮就大幅退燒,甚至還出現不少礦機被丟進垃圾場的照片。隨著去年比特幣、以太幣等加密貨幣再度大漲,AMD 與 NVIDA 桌機顯卡又開始出現短缺的情形,而且是過了半年還一樣,為此中國礦工開始將目標轉向 RTX 3000 系列遊戲筆電,最近就有人分享 RTX 3060 的挖礦測試,效率還相當不錯,看來在桌顯缺貨狀況尚未改善前,新款 RTX 3000 系列筆電搞不好也要面臨短缺現象了。

顯卡持續缺貨,中國加密貨幣礦工現在看上 RTX 3060 遊戲筆電

最近一位 JJJJKQ 中國網友於百度貼吧分享他實測 RTX 3060 Max-P 遊戲筆電的挖礦效率截圖,從數據來看,這顆晶片在挖以太幣(ETH)時效率可說相當不錯,GPU 算力為 48.99MH/s,溫度在 66 度上下。另外從配置來看,他有超頻 RTX 3060 Max-P,並不是以基礎效能下去挖礦:

GPU-Z 顯示,GPU 時脈為 1567.0MHz,記憶體時脈也來到 2125.2MHz,功耗為 104.7W:

就規格來說,RTX 3060 Max-P 行動顯示晶片跟 RTX 3060 桌顯比,TDP 雖然比較低(行動版 80W~130W、桌機 170W),不過 CUDA 核心 RTX 3060 Max-P 較多,來到 3,840 個,RTX 3060 桌顯則是 3,584 個,這也是為什麼拿來挖以太幣也能有不錯表現。

另外目前他測試的 RTX 3060 Max-P 功耗被限制在 104.7W,意味著如果提升到最高的 130W,挖礦效率應該還會更好。

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

RTX 3060 預計在 2 月下旬上市,還有半個多月的時間,因此在這之前,RTX 3000 系列筆電搞不好會先被礦工們掃購,導致缺貨的狀況出現,而且這討論串的回應中也有人提到,已經有 RTX 3060 筆電開始漲價了。

話說回來,筆電還是不太適合這樣長時間高速運轉,很容易導致內部硬體過熱燒壞。

資料來源:中國百度貼吧

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

【大廠面試03期】MySQL是怎麼解決幻讀問題的?_租車

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

問題分析

首先幻讀是什麼?

根據MySQL文檔上面的定義

The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.

幻讀指的是在一個事務內,同一SELECT語句在不同時間執行,得到不同的結果集時,就會發生所謂的幻讀問題。

可以看看下面的例子:

這是網上找的一張圖(事務的務字寫錯了,不過不影響我們理解)

假設這個例子中的MySQL的隔離級別是提交讀,也就是一個事務內可以讀到其他事務提交后的結果。

那麼事務1第一次查詢dept表中所有部門時,結果是沒有”研發部”,但是由於隔離級別是提交讀,在事務2插入“研發部”這一行數據后,並且提交后,事務1是可以讀取到的,所以第二次查詢時,結果集中會有“研發部”。這就是幻讀。

SELECT語句分類

首先我們的SELECT查詢分為快照讀和實時讀,快照讀通過MVCC(併發多版本控制)來解決幻讀問題,實時讀通過行鎖來解決幻讀問題。

快照讀

1.1 快照讀是什麼?

因為MySQL默認的隔離級別是可重複讀,這種隔離級別下,我們普通的SELECT語句都是快照讀,也就是在一個事務內,多次執行SELECT語句,查詢到的數據都是事務開始時那個狀態的數據(這樣就不會受其他事務修改數據的影響),這樣就解決了幻讀的問題。

1.2 那麼innodb是怎麼解決快照讀的幻讀問題的?

快照讀就是每一行數據中額外保存兩個隱藏的列,插入這個數據行時的版本號,刪除這個數據行時的版本號(可能為空),滾動指針(指向undo log中用於事務回滾的日誌記錄)。

事務在對數據修改后,進行保存時,如果數據行的當前版本號與事務開始取得數據的版本號一致就保存成功,否則保存失敗。

當我們不顯式使用BEGIN來開啟事務時,我們執行的每一條語句就是一個事務,每次開始事務時,會對系統版本號+1作為當前事務的ID。

1.2.1插入操作

插入一行數據時,將事務的ID作為數據行的創建版本號。

1.2.2刪除操作

執行刪除操作時,會將原數據行的刪除版本號設置為當前事務的ID,然後根據原數據行生成一條INSERT語句,寫入undo log,用於事務執行失敗時回滾。delete操作實際上不會直接刪除,而是將delete對象打上delete flag,標記為刪除,最終的刪除操作是purge線程完成的。但是會將數據行的刪除版本號設置為當前的事務的ID,這樣後面的事務B即便查到這行數據由於事務B的ID>刪除版本號,也會忽略這條數據。

1.2.3更新操作

更新時可以簡單的認為是先將舊數據刪除,然後插入一條新數據。

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

所以執行更新操作時,其實是會將原數據行的刪除版本號設置為當前事務的ID,生成一條INSERT語句,寫入undo log,用於事務執行失敗時回滾。插入一條新的數據,將事務的ID作為數據行的的創建版本號。

1.2.4查詢操作

數據行要被查詢出來必須滿足兩個條件,

  • 數據行刪除版本號為空或者>當前事務版本號的數據(否則數據已經被標記刪除了)

  • 創建版本號<=當前事務版本號的數據(否則數據是後面的事務創建出來的)

簡單來說,就是查詢時,

  • 如果該行數據沒有被加行鎖中的X鎖(也就是沒有其他事務對這行數據進行修改),那麼直接讀取數據(前提是數據的版本號<=當前事務版本號的數據,不然不會放到查詢結果集裏面)。
  • 該行數據被加了行鎖X鎖(也就是現在有其他事務對這行數據進行修改),那麼讀數據的事務不會進行等待,而是回去undo log端裏面讀之前版本的數據(這裏存儲的數據本身是用於回滾的),在可重複讀的隔離級別下,從undo log中讀取的數據總是事務開始時的快照數據(也就是版本號小於當前事務ID的數據),在提交讀的隔離級別下,從undo log中讀取的總是最新的快照數據。

1.3 補充資料:undo log段是什麼?

undo_log是一種邏輯日誌,是舊數據的備份。有兩個作用,用於事務回滾和為MVCC提供老版本的數據。

可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然,當update一條記錄時,它記錄一條對應相反的update記錄。

1.3.1.用於事務回滾

當事務執行失敗,回退時,會讀取這行數據的滾動指針(指向undo log中用於事務回滾的日誌記錄),就可以在undo log中找到相應的邏輯記錄,讀取到相應的回滾語句,執行進行回滾。

1.3.2.為MVCC提供老版本的數據

當讀取的某一行被其他事務鎖定時(也就是有其他事務正在改這行數據),它可以從undo log中分析出該行記錄以前的數據是什麼,從而提供該行版本信息,讓用戶進行快照讀。在可重複讀的隔離級別下,從undo log中讀取的數據總是事務開始時的快照數據(也就是版本號小於當前事務ID的數據),在提交讀的隔離級別下,從undo log中讀取的總是最新的快照數據(也就是比正在修改這行數據的事務ID修改前的數據。)。

實時讀

2.1實時讀是什麼?

如果說快照讀總是讀取事務開始時那個狀態的數據,實時讀就是查詢時總是執行這個查詢時數據庫中的數據。

一般使用以下這兩種查詢語句進行查詢時就是實時讀。

SELECT *** FOR UPDATE 在查詢時會先申請X鎖SELECT *** IN SHARE MODE 在查詢時會先申請S鎖

首先看一個實時讀產生幻讀的案例:

這是《MySQL技術內幕++InnoDB存儲引擎++第2版》裏面的一張圖,就是先將隔離級別設置為提交讀,這樣第一次執行 SELECT...FOR UPDATE查詢出來的數據是a:4,事務B插入了一條新的數據,再次執行 SELECT...FOR UPDATE語句時,查詢出來就是a:4,a:5兩條數據,這就是幻讀的問題。

2.1那麼innodb是怎麼解決實時讀的幻讀問題的?

如果我們不在一開始將將隔離級別設置為提交讀,其實是不會產生幻讀問題的,因為MySQL的默認隔離級別是可重複讀,在這種情況下,我們執行第一次 SELECT...FOR UPDATE查詢語句是,其實是會先申請行鎖,因為一開始數據庫就只有a:4一行數據,那麼加鎖區間其實是

(負無窮,4](4,正無窮)

我們查詢條件是a>2,上面兩個加鎖區間都會可能有數據滿足條件,所以會申請行鎖中的next-key lock,是會對上面這兩個區間都加鎖,這樣其他事務不能往這兩個區間插入數據,事務B會執行插入時會一直等待獲取鎖,直到事務A提交,釋放行鎖,事務B才有可能申請到鎖,然後進行插入。這樣就解決了幻讀問題。

如果大家對行鎖了解得比較少,下一期會對innodb中的鎖進行介紹。

最後

大家有什麼想法,可以一起討論!本文已收錄到1.1K Star數開源學習指南——《大廠面試指北》,如果想要了解更多大廠面試相關的內容,了解更多可以看
http://notfound9.github.io/interviewGuide/#/docs/BATInterview

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

戴著口罩無法顏面辨識 傳蘋果將在新機合併Face ID與Toch ID_租車

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

由於武漢肺炎肆虐的關係,口罩基本上已經變成生活的標準配備,許多人出門就是把口罩戴的緊緊的。雖然在健康上提升了不少安全,但是對於那些仰賴顏面辨識的手機來講,就添增不少困擾,不少人要解鎖手機的時候,都還得拉下口罩,著實有點不方便。而近日傳出,蘋果官方似乎打算在新一代的iPhone中,將原本的Touch ID與Face ID做個合併,讓大家可以更輕鬆的幫手機解鎖。

↑口罩的佩戴,真的造成許多顏面辨識功能無法使用。(示意圖/pixabay)

根據《9to5mac》報導指出,其實蘋果對於合併Touch ID與Face ID這事情,已經計畫了好幾年。但由於螢幕限制的關係,這個想法一直沒辦法付諸實行。而在1月中旬時,《彭博社》報導指出,蘋果目前正在開發「螢幕指紋辨識器」,似乎是有意思將Toch ID重新回歸市場。而在報導中指出,這款具備Touch ID與Face ID的iPhone即將上市。

 

再根據《華爾街日報》報導指出,有兩名蘋果的前員工表示,蘋果內部一直在進行螢幕指紋辨識器的研究,並且明確的考慮在一個設備上「同時安裝Touch ID與Face ID」。

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

 

《9to5mac》認為,在今年的iPhone新機上,應該不用太過期待會有亮眼的新功能,所以像是「Touch ID與Face ID」這樣同步合併的新功能,會變成一個不錯的選項,尤其武漢肺炎看樣子在今年應該無法歇息,所以這樣的功能應該也會受到使用者的歡迎。同時,《9to5mac》表示,今年的iPhone有可能會移除Lightning插孔,變成使用USB Type-C來取代。

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

從源碼研究如何不重啟Springboot項目實現redis配置動態切換_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

上一篇Websocket的續篇暫時還沒有動手寫,這篇算是插播吧。今天講講不重啟項目動態切換redis服務。

背景

多個項目或微服務場景下,各個項目都需要配置redis數據源。但是,每當運維搞事時(修改redis服務地址或端口),各個項目都需要進行重啟才能連接上最新的redis配置。服務一多,修改各個項目配置然後重啟項目就非常蛋疼。所以我們想要找到一個可行的解決方案,能夠不重啟項目的情況下,修改配置,動態切換redis服務。

如何實現切換redis連接

剛遇到這個問題的時候,想必如果對spring-boot-starter-data-redis不是很熟悉的人,首先想到的就是去百度一下(安慰下自己:不要重複造輪子嘛)。

可是一陣百度之後,你找到的結果可能都是這樣的:

public ValueOperations updateRedisConfig() {
    JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) stringRedisTemplate.getConnectionFactory();
    jedisConnectionFactory.setDatabase(db);
    stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);
    ValueOperations valueOperations = stringRedisTemplate.opsForValue();
    return ValueOperations;

沒錯,絕大多數都是切換redis db的代碼,而沒有切redis服務地址或賬號密碼的。而且天下代碼一大抄,大多數博客都是一樣的內容,這就讓人很噁心。

沒辦法,網上沒有,只能自己造輪子了。不過,從強哥這種懶人思維來說,上面的代碼既然能切庫,那是不是host、username、password也同樣可以,於是我們加入如下代碼:

public ValueOperations updateRedisConfig() {
    JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) stringRedisTemplate.getConnectionFactory();
    jedisConnectionFactory.setDatabase(db);
    jedisConnectionFactory.setHostName(host);
    jedisConnectionFactory.setPort(port);
    jedisConnectionFactory.setPassword(password);
    stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);
    ValueOperations valueOperations = stringRedisTemplate.opsForValue();
    return valueOperations;
}

話不多說,改完重啟一下。額,運行結果並沒有讓我們見證奇迹的時刻。在調用updateRedisConfig方法的之後,使用redisTemplate還是只能切換db,不能進行服務地址或賬號密碼的更新。

這就讓人頭疼了,不過想也沒錯,如果可以的話,網上不應該找不到類似的代碼。那麼,現在該咋辦嘞?

強哥的想法是:redisTemplate每次獲取ValueOperations執行get/set方法的時候,都會去連接redis服務器,那麼我們就從這兩個方法入手看看能不能找得到解決方案。

接下來就是源碼研究的過程啦,有耐心的小夥伴就跟着強哥一起找,只想要結果的就跳到文末吧~

首先來看看入手工具方法set:

 
public boolean set(final String key, Object value) {
  boolean result = false;
  try {
          ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
          operations.set(key, value);
          result = true;
      } catch (Exception e) {
          logger.error("set cache error:", e);
      }
  return result;
}

我們進入到operations.set(key, value);的set方法實現:

public boolean set(String key, Object value) {
        boolean result = false;
    try {
        ValueOperations<Serializable, Object> operations = this.redisTemplate.opsForValue();
        operations.set(key, value);
        result = true;
    } catch (Exception var5) {
      this.logger.error("set error:", var5);
    }
    return result;
}

哦,走的是execute方法,進去看看,具體調用的是AbstractOperations的RedisTemplate的execute方法(中間跳過幾個重載方法跳轉):

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
    Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
    Assert.notNull(action, "Callback object must not be null");
    RedisConnectionFactory factory = getConnectionFactory();
    RedisConnection conn = null;
    try {
      if (enableTransactionSupport) {
// only bind resources in case of potential transaction synchronization
        conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
      } else {
        conn = RedisConnectionUtils.getConnection(factory);
      }
      boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
      RedisConnection connToUse = preProcessConnection(conn, existingConnection);
      boolean pipelineStatus = connToUse.isPipelined();
      if (pipeline && !pipelineStatus) {
        connToUse.openPipeline();
      }
      RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
      T result = action.doInRedis(connToExpose);
      // close pipeline
      if (pipeline && !pipelineStatus) {
        connToUse.closePipeline();
      }
      // TODO: any other connection processing?
      return postProcessResult(result, connToUse, existingConnection);
    } finally {
      RedisConnectionUtils.releaseConnection(conn, factory);
    }
}

方法內容很長,不過大致可以看出前面是獲取一個RedisConnection對象,後面應該就是命令的執行,為什麼說應該?因為強哥也沒去細看後面的實現,因為我們要關注的就是怎麼拿到這個RedisConnection對象的。

那麼我們走RedisConnectionUtils.getConnection(factory);這句代碼進去看看,為什麼我知道是走這句而不是上面那句,因為強哥沒開事務,如果大家有打斷點,應該默認也是走的這句,跳到具體的實現方法:RedisConnectionUtils.doGetConnection(……):

public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind,
boolean enableTransactionSupport) {
    Assert.notNull(factory, "No RedisConnectionFactory specified");
    RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);
    if (connHolder != null) {
      if (enableTransactionSupport) {
        potentiallyRegisterTransactionSynchronisation(connHolder, factory);
      }
      return connHolder.getConnection();
    }
    if (!allowCreate) {
      throw new IllegalArgumentException("No connection found and allowCreate = false");
    }
    if (log.isDebugEnabled()) {
      log.debug("Opening RedisConnection");
    }
    RedisConnection conn = factory.getConnection();
    if (bind) {
      RedisConnection connectionToBind = conn;
      if (enableTransactionSupport && isActualNonReadonlyTransactionActive()) {
        connectionToBind = createConnectionProxy(conn, factory);
      }
      connHolder = new RedisConnectionHolder(connectionToBind);
      TransactionSynchronizationManager.bindResource(factory, connHolder);
      if (enableTransactionSupport) {
        potentiallyRegisterTransactionSynchronisation(connHolder, factory);
      }
      return connHolder.getConnection();
    }
    return conn;
  }

代碼還是很長,話不多說,斷點走的這句:RedisConnection conn = factory.getConnection();那就看看其實現方法吧:JedisConnectionFactory.getConnection(),這個是個關鍵方法:

public RedisConnection getConnection() {
 if (cluster != null) {
   return getClusterConnection();
 }
 Jedis jedis = fetchJedisConnector();
 JedisConnection connection = (usePool ? new JedisConnection(jedis, pool, dbIndex, clientName)
     : new JedisConnection(jedis, null, dbIndex, clientName));
 connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
 return postProcessConnection(connection);
}

看到了,代碼很短,但是我們從中可以獲取到的內容卻很多:

第一個判斷是是否有集群,這個強哥項目暫時沒用,所以不管;如果大家有用到,可能要要考慮下裏面的代碼。

Jedis對象是在這裏創建的,熟悉redis的應該都知道:Jedis是Redis官方推薦的Java連接開發工具。直接用它就能執行redis命令。

usePool 這個變量,說明我們連接的redis服務器的時候可能用到了連接池;不知道大家看到usePool會不會有種恍然醒悟的感覺,很可能就是因為我們使用了連接池,所以即使我們之前的代碼中切換了賬號密碼,連接池的連接還是沒有更新導致的處理無效。

我們先看看fetchJedisConnector方法實現:

protected Jedis fetchJedisConnector() {
  try {
    if (usePool && pool != null) {
      return pool.getResource();
    }
 
    Jedis jedis = new Jedis(getShardInfo());
  // force initialization (see Jedis issue #82)
    jedis.connect();
  
    potentiallySetClientName(jedis);
    return jedis;
  } catch (Exception ex) {
throw new RedisConnectionFailureException("Cannot get Jedis connection", ex);
  }
}

哦,可以看到,Jedis對象是根據getShardInfo()構建出來的:

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

public BinaryJedis(JedisShardInfo shardInfo) {
  this.client = new Client(shardInfo.getHost(), shardInfo.getPort(), shardInfo.getSsl(), shardInfo.getSslSocketFactory(), shardInfo.getSslParameters(), shardInfo.getHostnameVerifier());
  this.client.setConnectionTimeout(shardInfo.getConnectionTimeout());
  this.client.setSoTimeout(shardInfo.getSoTimeout());
  this.client.setPassword(shardInfo.getPassword());
  this.client.setDb((long)shardInfo.getDb());
}

那就是說,只要我們掌握了這個JedisShardInfo的由來,我們就可以實現redis相關配置的切換。而這個getShardInfo()方法就是返回了JedisConnetcionFactory類的JedisShardInfo shardInfo屬性:

public JedisShardInfo getShardInfo() {
  return shardInfo;
}

那麼如果我們知道了這個shardInfo是如何創建的,是不是就可以干預到RedisConnect的創建了呢?我們來找找它被創建的地方:

走的JedisConnectionFactory的afterPropertiesSet()進去看看:

/*
  * (non-Javadoc)
  * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
  */
public void afterPropertiesSet() {
 if (shardInfo == null) {
   shardInfo = new JedisShardInfo(hostName, port);
   if (StringUtils.hasLength(password)) {
     shardInfo.setPassword(password);
   }  
   if (timeout > 0) {
       setTimeoutOn(shardInfo, timeout);
     }
   }

   if (usePool && clusterConfig == null) {
     this.pool = createPool();
   }
 
   if (clusterConfig != null) {
     this.cluster = createCluster();
   }
}

哦吼~,整篇博文最關鍵的代碼終於出現了。我們可以看到,JedisShardInfo的所有信息都是從JedisConnetionFactory的屬性中來的,包括hostName、port、password、timeout等。而且,如果JedisShardInfo為null時,調用afterPropertiesSet方法會幫我們創建出來。然後,該方法還會幫我們創建新的連接池,簡直完美。最最重要的是,這個方法是public的。

所以,嘿嘿,綜上,我們總結改造的幾個點:

1.連接redis用到了連接池,需要先給他銷毀;

2.創建Jedis的時候,將JedisShardInfo先設為null;

3.手動設置JedisConnetionFactory的hostName、port、password等信息;

4.調用JedisConnetionFactory的afterPropertiesSet方法創建JedisShardInfo;

5.給RedisTemplate設置處理后的JedisConnetionFactory,這樣在下次使用set或get方法的時候就會去創建新改配置的連接池啦。

實現如下:

public void updateRedisConfig() {
  RedisTemplate template = (RedisTemplate) applicationContext.getBean("redisTemplate");
  JedisConnectionFactory redisConnectionFactory = (JedisConnectionFactory) template.getConnectionFactory();
//關閉連接池
  redisConnectionFactory.destroy();
  redisConnectionFactory.setShardInfo(null);
  redisConnectionFactory.setHostName(host);
  redisConnectionFactory.setPort(port);
  redisConnectionFactory.setPassword(password);
  redisConnectionFactory.setDatabase(database);
  //重新創建連接池
  redisConnectionFactory.afterPropertiesSet();
  template.setConnectionFactory(redisConnectionFactory);
}

重啟項目之後,調用這個方法,就可以實現redis庫及服務地址、賬號密碼的切換而無需重啟項目了。

如何實現動態切換

強哥這裏就使用同一配置中心Apollo來進行動態配置的。

首先不懂Apollo是什麼的同學,先Apollo官網半日游吧(直接看官網教程,比看其他博客強)。簡單的說就是一個統一配置中心,將原來配置在項目本地的配置(如:Spring中的application.properties)遷移到Apollo上,實現統一的管理。

使用Apollo的原因,其實就是因為其接入簡單,且具有實時更新回調的功能,我們可以監聽Apollo上的配置修改,實現針對修改的配置內容進行相應的回調監聽處理。

因此我們可以將redis的配置信息配置在Apollo上,然後監聽這些配置。當Apollo上的這些配置修改時,我們在ConfigChangeListener中,調用上面的updateRedisConfig方法就可以實現redis配置的動態切換了。

接入Apollo代碼非常簡單:

Config redisConfig = ConfigService.getConfig("redis");
ConfigChangeListener listener = this::updateRedisConfig;
redisConfig.addChangeListener(listener);

這樣,我們就可以實現具體所謂的動態更新配置啦~

當然,其他有相同功能的配置中心其實也可以,只是強哥項目中暫時用的就是Apollo就拿Apollo來講了。

考慮到篇幅已經很長了,就不多解釋Apollo的使用了,用過的自然看得懂上面的方法,有不懂的也可以留言提問哦。

好了,就到這吧,原創不易,怎麼支持你們知道,那麼下次見啦

關注公眾號獲取更多內容,有問題也可在公眾號提問哦:

強哥叨逼叨

叨逼叨編程、互聯網的見解和新鮮事

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

小米11 國際版將於2/8 晚間發表,有望 3 月在台亮相_潭子電動車

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

在 2020 年 12 月底,小米於中國發表首款搭載高通 Snapdragon 888 處理器的旗艦手機「小米11」,截至目前小米11 也只在中國當地銷售。針對中國以外的市場方面,小米11 國際版除了日前通過 NCC 認證,近期也正式宣佈將於 2 月 8 日晚上 8 點舉行小米11 國際版發表會,未來小米11 也將引進台灣市場販售。

小米11 國際版將於2/8 晚間發表,有望 3 月在台亮相

小米11 國際版多數規格預計和去年底發表的中國版的關鍵關格大致相似,像是搭載 Qualcomm Snapdragon 888 處理器、 6.81 吋 2K(3200×1440 WQHD)E4 材質 AMOLED 四曲面柔性螢幕。
螢幕支持最高 120Hz 螢幕更新率、480Hz 觸控採樣率, 1500nit 峰值亮度、480Hz  觸控採樣率、5,000,000:1 對比度,螢幕也擁有 100% P3 色域和 HDR10+ 認證。
另外,小米11 螢幕玻璃採用康寧 Gorilla Glass Victus 玻璃保護,抗摔性相較前代提升 1.5倍、耐刮性能提升 2 倍。

相機方面,小米 11 採用 1 億 800 萬像素三鏡頭主相機設計,分別為 1 億 800 萬像素像素(1 / 1.33″超大感光元件)標準鏡頭、1300 萬像素 123° 超廣角鏡頭、 500 萬像素 50mm 微距長焦鏡頭,前置鏡頭則配備 2000 萬像素自拍相機。
此外,小米11 內建等效 4600mAh 大電池,支持 55W 有線閃充、50W 無線閃充以及10W 反向無線充電, 55W 有線閃充可在 45 分鐘充滿 100% 電量、50W 無線閃充可在 53 分鐘充滿 100% 電量。

▲圖片來源:小米(中國)

與小米11 中國版最大的差異在於小米11 國際版將加入 GMS 支援,不過目前還無法得知將推出哪幾款配色與容量選擇。雖然,小米11 國際版在下週 2 月 8 日晚上 8 點就會發表,不過台灣市場預計最快要等到 3 月才會引進銷售。

小米11 國際版線上發表會(2021/2/8 20:00開始)

 

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

延伸閱讀:
多款旗艦手機電力續航能力實測,三星 Galaxy S21 Ultra 仍敗給 iPhone 12 Pro Max

小米隔空充電技術正式發表:手持也能隔空充電,「真」無線充電時代來臨!

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

26萬你會買GS8高配,還是冠道低配?哪個配置性價比最高?

5T的車型上市,售價更低,在看來已經基本與昂科威重合,相信日後在合資SUV榜首位置的競爭中將會非常精彩。哪個配置值得入手。320T兩驅豪華版官方指導價:16。98萬推薦指數:320T兩驅豪華版是傳祺GS8的入門版7座版本,在配置方面作為一款國產車型並不會低,大量的標配的配置成為了最大的亮點,真皮電動座椅、自動空調、后視鏡電動摺疊、後排出風口等都是國人選車的首選配置。

如果有人問最近在有什麼好的SUV可以推薦一下,或者會毫不疑問地說出了以下這兩款車型:第一就是本田的冠道,第二就是傳祺的GS8,對於這兩款車型,相信不少消費者都會為此動容,然而究竟買什麼配置才是最合適的呢?請聽一一道來。

究竟買哪個配置性價比最高?

370TURBO 豪華版

官方指導價:27.98萬

推薦指數:

豪華版在冠道車型車的次低配車型,27.98萬的售價讓整車落地的價格已經邁向了30萬這個幅度,但是在配置方面已經能滿足99%消費者的用車需求,LED大燈/霧燈、全景天窗、電動尾門、等都是提高整車逼格的配置,加上大量全系標配的配置讓豪華版的冠道性價比十足,逼格滿滿,如果不是對四驅有硬性要求,選擇豪華版是最具性價比的。

本田冠道真的值得入手嗎?

在看來,雖然很多人將本田冠道的對手放在了漢蘭達和銳界上,但只有5座版本的它在別人眼中還是一種遺憾,而實際上5座版本的好處是可以6年免檢!另外漢蘭達、銳界就算是7座版本,第三排空間太少作用也不大!所以在看來冠道的最直接競爭對手還是別克昂科威。而對於本田冠道這個車型,最讓深刻的是這套讓其能夠讓這車跑出8秒內的2.0T+9AT的動力總成以及38米左右的制動成績,在明年還會有1.5T的車型上市,售價更低,在看來已經基本與昂科威重合,相信日後在合資SUV榜首位置的競爭中將會非常精彩。

哪個配置值得入手?

320T兩驅豪華版

官方指導價:16.98萬

推薦指數:

320T兩驅豪華版是傳祺GS8的入門版7座版本,在配置方面作為一款國產車型並不會低,大量的標配的配置成為了最大的亮點,真皮電動座椅、自動空調、后視鏡電動摺疊、後排出風口等都是國人選車的首選配置。

320T 豪華智聯版(兩驅/四驅)

官方指導價:兩驅18.28萬、四驅19.98萬

推薦指數:

320T智聯版比豪華版加入了更多高逼格實用的配置,全LED大燈、19寸輪轂、全景天窗、方向盤加熱、外后視鏡下翻、導航、併線輔助、電動尾門(四驅版本)等均配備在GS8上,這可是比上一位选手本田冠道擁有更高逼格的配置,值得一說的是,它的售價還不到20萬!所以高性價比、高顏值的外觀,無疑智聯版是最值得選擇的車型。

廣汽傳祺GS8真的值得入手嗎?

在看來性價比十足!傳祺GS8的售價、配置都能夠讓人很滿意,用買途觀的價格買到一台高顏值、高配置的大7座SUV對於追求實用的消費者來說非常棒,在看來這樣一台中型SUV就好比合資車型中的漢蘭達。另外傳祺一路以來的口碑是相當不錯的,GS4已經算是一個很好的例子,而且這一次放棄使用雙離合改用2.0T+6AT的動力總成,也能最大程度減少了消費者對於雙離合的爭議。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

Python 圖像處理 OpenCV (6):圖像的閾值處理

前文傳送門:

「Python 圖像處理 OpenCV (1):入門」

「Python 圖像處理 OpenCV (2):像素處理與 Numpy 操作以及 Matplotlib 显示圖像」

「Python 圖像處理 OpenCV (3):圖像屬性、圖像感興趣 ROI 區域及通道處理」

「Python 圖像處理 OpenCV (4):圖像算數運算以及修改顏色空間」

「Python 圖像處理 OpenCV (5):圖像的幾何變換」

圖像的閾值

看到這個詞可能大家都很懵,為啥在圖像處理裏面還會有閾值。

圖像的閾值處理用大白話講就是將圖像轉化為二值圖像(黑白圖),目的是用來提取圖像中的目標物體,將背景和噪聲區分開(可以近似的認為除了目標全是噪聲)。

通常會設定一個閾值 T ,通過 T 將圖像的像素劃分為兩類:大於 T 的像素群和小於 T 的像素群。

首先可以先將圖像轉化為灰度圖像,因為在灰度圖像中,每個像素都只有一個灰度值用來表示當前像素的亮度。

接下來二值化處理可以將圖像中的像素劃分為兩類顏色,一種是大於閾值 T 的,另一種是小於閾值 T 的。

比如最常見的二值圖像:

當灰度值小於閾值 T 的時候,可以將其像素設置為 0 ,表示為黑色。

當灰度值大於閾值 T 的時候,可以將其像素設置為 255 ,表示為白色。

在 OpenCV 中,為我們提供了閾值函數 threshold() 來幫助我們實現二值圖像的處理。

函數如下:

retval, dst = threshold(src, thresh, maxval, type, dst=None)
  • retval: 閾值
  • dst: 處理后的圖像
  • src: 原圖像
  • thresh: 閾值
  • maxval: 最大值
  • type: 處理類型

常用的 5 中處理類型如下:

  • cv.THRESH_BINARY: 二值處理
  • cv.THRESH_BINARY_INV: 反二值處理
  • cv.THRESH_TRUNC: 截斷閾值化
  • cv.THRESH_TOZERO: 閾值化為 0
  • cv.THRESH_TOZERO_INV: 反閾值化為 0

接下來這幾種處理類型有啥不同,我們一個一個來看。

二值處理

這種二值處理方式最開始需要選定一個閾值 T ,從 0 ~ 255 之間,我這裏選擇出於中間的那個數 127 。

接下來的處理規則就是這樣的:

  • 大於等於 127 的像素點的灰度值設定為最大值,也就是 255 白色
  • 小於 127 的像素點的灰度值設定為 0 ,也就是黑色

接下來開始寫代碼,看我們的馬里奧同學(不知道你們還記不記得我們的馬里奧同學):

import cv2 as cv

src = cv.imread("maliao.jpg")

# BGR 圖像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 二值圖像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY)

# 显示圖像
cv.imshow("src", src)
cv.imshow("result", b)

# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()

反二值處理

這種方式和上面的二值處理非常相似,只是把處理規則給反了一下:

  • 大於等於 127 的像素點的灰度值設定為 0 ,也就是白色
  • 小於 127 的像素點的灰度值設定為最大值,也就是 255 白色

完整代碼如下:

import cv2 as cv

src = cv.imread("maliao.jpg")

# BGR 圖像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 二值圖像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY_INV)

# 显示圖像
cv.imshow("src", src)
cv.imshow("result", b)

# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()

從圖像上可以看到,顏色和上面的二值圖像正好相反,大部分的位置都變成了白色。

截斷閾值化

這種方法還是需要先選定一個閾值 T ,圖像中大於該閾值的像素點被設定為該閾值,小於該閾值的保持不變。

完整代碼如下:

import cv2 as cv

src = cv.imread("maliao.jpg")

# BGR 圖像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 二值圖像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TRUNC)

# 显示圖像
cv.imshow("src", src)
cv.imshow("result", b)

# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()

這種方式實際上是把圖片比較亮的像素處理成為閾值,其他部分保持不變。

閾值化為 0

這種方式還是需要先選定一個閾值 T ,將小於 T 的像素點設置為 0 黑色,其他的保持不變。

完整代碼如下:

import cv2 as cv

src = cv.imread("maliao.jpg")

# BGR 圖像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 二值圖像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO)

# 显示圖像
cv.imshow("src", src)
cv.imshow("result", b)

# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()

這個方法是亮的部分不改,把比較暗的部分修改為 0 。

反閾值化為 0

這個和前面的反二值圖像很像,同樣是反閾值化為 0 ,將大於等於 T 的像素點變為 0 ,其餘保持不變。

完整代碼如下:

import cv2 as cv

src = cv.imread("maliao.jpg")

# BGR 圖像轉灰度
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

# 二值圖像處理
r, b = cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO_INV)

# 显示圖像
cv.imshow("src", src)
cv.imshow("result", b)

# 等待显示
cv.waitKey(0)
cv.destroyAllWindows()

這個方法是暗的部分不改,把比較亮的部分修改為 0 。

全家福

接下來還是給這幾種閾值處理后的圖像來個全家福,讓大家能有一個直觀的感受,代碼我也給出來,如下:

import cv2 as cv
import matplotlib.pyplot as plt

# 讀取圖像
img=cv.imread('maliao.jpg')
lenna_img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
gray_img=cv.cvtColor(img,cv.COLOR_BGR2GRAY)

# 閾值化處理
ret1, thresh1=cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY)
ret2, thresh2=cv.threshold(gray_img, 127, 255, cv.THRESH_BINARY_INV)
ret3, thresh3=cv.threshold(gray_img, 127, 255, cv.THRESH_TRUNC)
ret4, thresh4=cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO)
ret5, thresh5=cv.threshold(gray_img, 127, 255, cv.THRESH_TOZERO_INV)

# 显示結果
titles = ['Gray Img','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [gray_img, thresh1, thresh2, thresh3, thresh4, thresh5]

# matplotlib 繪圖
for i in range(6):
   plt.subplot(2, 3, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

示例代碼

如果有需要獲取源碼的同學可以在公眾號回復「OpenCV」進行獲取。

參考

https://blog.csdn.net/Eastmount/article/details/83548652

http://www.woshicver.com/

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

廣州現場精彩不斷 奧德賽主場熱力開局

“全能MpV”奧德賽具備了魔方式的空間變換組合,可以根據不同場合的需求自由切換:除了可以乘坐7人的正常模式以外,還可以放倒部分第三排座椅,變為“休閑模式”或“超長模式”,更能將後排完全收起放平,變為“超大模式”。

在個人色彩越來越濃的今天

我們在職場上八面玲瓏,進退自如

但一轉身,面對與最親近的家人溝通

卻往往不知該如何應對……

11月的廣州依舊“熱情”高漲,而這份熱情也同樣延續到了11月12日在廣州舉辦的奧德賽非凡品鑒會活動現場。作為繼上海、北京之後又一場有別傳統的非凡品鑒會,坐鎮主場的奧德賽此番同樣延續了熱播綜藝《飯局的誘惑》節目熱潮,誠邀兩位金牌辯手現場與大家分享傳授說話之道,在欣賞奧德賽非凡魅力的同時,現場觀眾更是得到了智、情、局三商的全方位提升。

事實上,《飯局的誘惑》作為奧德賽與綜藝大咖馬東聯手打造的一檔綜藝節目,節目中高智商高情商的語言對壘令觀眾朋友大呼過癮。同樣的,以雙商詮釋自我產品特點的奧德賽在節目中頻繁亮相,更是成為嘉賓主持話梗里的經典“橋段”。也正因此,在非凡品鑒會上,奧德賽同樣將“說話的藝術”搬到了現場,在全方位展示自身產品實力的同時,更是將智、情、局三商的精髓應用到了日常生活之中。

延續高雙商開局 ,奧德賽掌控生活格局

Talk show環節,一如在前兩站中,“金牌辯手”們教會了大家如何應付生活中的各種尷尬,廣州站品鑒會上,馬薇薇與黃執中同樣為大家準備了新鮮的生活話題。

現場,金句女王馬薇薇首先與大家分享了如何在家庭生活中以“分”為貴,即在家庭關係中分清是“男權”還是“平權”為主;分清楚想要從原生家庭得到什麼就必須失去什麼;分清楚在自己組建的新家庭里是願意當主人還是當“孩子”。把握家庭話語權,例如馬薇薇提及在平權家庭中,面對出行問題時則必須共同承擔,在帶上自己父母的同時更要照顧到對方的父母。也正因此,像奧德賽這樣至少能夠容下6個人的車才是出行的必需品。

而事實上,作為一款7座MpV車型,奧德賽擁有2+2+3的座椅布局,在日常出行中,與一家三口的需求完美匹配,可以盡享第二排航空座椅的舒適體驗。即便是再帶上雙方父母,奧德賽的座位和空間也是綽綽有餘。這便是奧德賽對於家庭出行更為全面的考慮,也足以凸顯出其高情商的一面。

對於家庭生活的妥善處理,在網絡上一向以“情感專家”著稱的黃執中更是以男性視角深入剖析,教會大家如何利用情商照顧到身邊的每一個人。在談話中,黃執中提出:在遇到問題時不應關注事件本身,而是當事人雙方感覺的問題;在討論問題時不應站在對立面,而是轉換方式,一同面對問題;最終即便問題解決,也應該從對方得失心出發給予一定的安慰,讓對方知道她的委屈你能懂。簡而言之,黃執中用超高的情商詮釋了面對尷尬處境時如何照顧全局,細緻巧妙的處理也贏得了在場觀眾的掌聲。

正如黃執中說的那樣,奧德賽對於“掌控全局”也同樣非常在行。“全能MpV”奧德賽具備了魔方式的空間變換組合,可以根據不同場合的需求自由切換:除了可以乘坐7人的正常模式以外,還可以放倒部分第三排座椅,變為“休閑模式”或“超長模式”,更能將後排完全收起放平,變為“超大模式”。無論是全家外出的座位需要,亦或是商務接待需要的車內空間,奧德賽都能輕鬆掌控。

情景演繹扣人心弦,奧德賽解碼非凡亮點

與傳統品鑒會相比,非凡品鑒會的現場可謂處處不同。除了引人入勝的talk show環節,現場講師對產品的解讀也是別具一格。通過場景的植入與話題的點撥,講師從“撥、移、躺、藏、開”多角度深入,為現場觀眾生動解碼了奧德賽的非凡體驗點。

以“移”為例,在講解過程中,講師通過實際的例子為大家詮釋了奧德賽超低車門踏板設計的用意。原來,在設計之初,考慮到一些身着裙裝的女性能夠方便上下車,同時也照顧到老人小孩的乘坐便利,奧德賽在保證通過性的同時,將油箱做的更扁平,合理化配置的前提下,為乘客提供了最佳的上下車高度。奧德賽用高智商和高情商的雙商完美解決了問題。

亦動亦靜,全方位感受奧德賽非凡一面

靜態展示體驗部分,現場的一輛“特殊”展車令不少人眼前一亮。作為企業社會化責任的一部分,同時也作為車系派生車型的進一步完善,奧德賽福祉車的亮相無疑是場上的另一大看點。通過遙控二排可升降旋轉座椅,短短十幾秒內,行動不便的乘客無需自己挪動便可輕鬆實現上下車動作,方便之餘也再次體現出奧德賽全方位人性化的設計理念。

當然,除了靜態展示部分,品鑒會又怎少的了試駕環節。不同與上海站與北京站的路況,廣州站的試駕路線採用了“直線”加“調頭”的模式。試駕時,體驗者不僅能夠感受到“黃金動力組合”下奧德賽CVT無級變速箱的平順加速動力,同時,在調頭部分中更可感受奧德賽同級最小轉彎半徑的優勢。這樣的專項試駕也令不少體驗者印象深刻。

除了靜態展示和試乘試駕部分外,主辦方還為親臨現場體驗的來賓準備了奧德賽全能生活非凡禮遇。豐盛的現金禮遇和置換福利表現也令不少來賓現場簽訂購車意向。奧德賽的高局商則恰恰體現在此。

從職場到生活,從同事相處到家庭關係,奧德賽攜多位名嘴辯手與大家分享了職場人生中的說話之道,用一個個生動有趣的例子完美詮釋了高智商高情商的人生格局。創新、趣味、智慧、尊享,這就是奧德賽非凡品鑒會留給在場所有人的印象。而下一站深圳,活動即將迎來收官站,屆時,名嘴嘉賓又將有哪些辛辣犀利的話題與大家分享,奧德賽與你一同見證非凡本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準