本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
文章來源:工信部
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
摘錄自2020年2月6日ETtoday新聞雲報導
澳洲昆士蘭洲近日有25萬到30萬隻的黑狐蝠因為大火的關係,從牠們棲息的山洞逃出,湧入澳洲的各個城市。澳洲的黑狐蝠長著「狗頭」,翅膀展開可達1.5米,有「毒王」之稱,領地意識相當強,連人類的脖子都可以咬斷。
綜合外媒報導,這次大約有30萬隻蝙蝠,從澳洲昆士蘭州的多個城市上空飛過,逃往它們適合生存的地方,頓時整個城市上空佈滿了黑壓壓的蝙蝠隊伍。由於蝙蝠實在太多了,還導致一架醫療救援直升機不敢降落。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益
摘錄自2020年02月07日中央通訊社澳洲報導
澳洲今(7日)傾盆大雨,為終止前所未見的野火危機帶來希望,雨水澆熄了失控延燒數個月的火勢,但也讓當局擔心隨之而來的水災。
災情最嚴重的新南威爾斯州(New South Wales)本週稍早部分地區開始降雨,並預計會持續至下週,料將澆熄數場剩餘大火,並有助於控制其他火勢。
到今天下午,新南威爾斯州沒有最危險緊急狀況或「注意和須採取行動」等級的野火,是將近半年未曾見過的狀況。
不過,隨著野火危機消退,當局現在準備可能發生的水災。澳洲氣象局(Australian Bureau of Meteorology)發布一系列水災警告,並表示新南威爾斯部分地區將出現4年來最大降雨。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:Carbon Brief
研究發現,全世界熱帶森林從大氣中吸收二氧化碳的能力正在衰退,而位於高緯度寒冷地區的北方森林,吸收二氧化碳的速度正在增加。
這項新研究結合遙測資料和數學模型,詳細揭露1992年至2015年地球上所有生物區系的碳損失和碳吸收。研究顯示,世界上最重要的兩個陸地儲碳生態系統——熱帶雨林和北寒林,正在發生變化。
位於高緯度寒冷地區的北方森林,吸收二氧化碳的速度正在增加。照片來源:IBM(CC BY-NC-ND 2.0)
作者表示,研究探討的這段期間,熱帶森林碳損失的主要驅動力是森林砍伐。受影響特別嚴重的地區包括、印尼和東南亞。
另一位科學家表示,目前還不清楚是什麼因素使北寒林的碳增加。一個可能性是二氧化碳施肥效應,也就是大氣中二氧化碳含量增加促進植物生長。
整體而言,研究結果顯示熱帶森林吸收二氧化碳的能力衰退,相當令人擔憂。人類活動產生的溫室氣體排放約有30%被土地吸收,使其成為重要的「」。當樹木和其他類型的植群進行光合作用時,土地從大氣中吸收二氧化碳。植物利用二氧化碳長出枝條、根和葉。也就是說,只要植物還活著,就是長期的碳匯。
這項新的發表在《自然生態與演化(Nature Ecology and Evolution)》期刊上,使用多種技術來繪製1992年至2015年全球所有生物區系的碳損失和碳吸收狀況,包括旱地、稀疏植生地、苔原(一種北極環境),以及溫帶林、北寒林和熱帶森林。
(溫帶森林所在地氣候溫和,四季分明,而北寒林則位於較冷的高緯度地區,特徵是常綠的松樹、雲杉和落葉松。)
下圖顯示了研究中所含的各種生物區系分布。溫帶、寒帶和熱帶生態系統進一步細分為「低矮植生地」或「森林」。「低矮植生地」表示未被原始森林覆蓋,地景以草、農田、灌木或莽原為主的區域。
1992-2015年世界陸地生物群系分布,包括稀疏植生地(黃色)、苔原(藍色)、北寒低矮植生地(淺綠色)、北寒林(深綠色)、溫帶低矮植生地(橄欖)、溫帶森林(黑色)、旱地 (橙色)、熱帶低矮植生地(綠松石)、熱帶森林(深藍色)和裸露地(灰色)。 資料來源:Tagesson et al. (2020)
作者結合數學模型和遙測資料分析每個地區的碳儲量。這些資料來自微波偵測「地表生物量」變化的衛星。地表生物量是覆蓋陸地表面的所有活植物的生物量,包含樹枝、樹葉、樹幹和落葉。
呼應過去研究,該研究發現熱帶森林和北寒林是最重要的儲碳生物群落。研究期間範圍內,這兩個生物群系合計佔土地儲碳的一半以上(53%)。
然而,來自瑞典隆德大學的主要作者托本.塔格森(Torben Tagesson)博士說,這兩個地區的碳儲存能力有所差異。「研究結果讓我們掌握二氧化碳吸收量在世界各地的分布情況,並顯示熱帶森林的貢獻正在大大減少。同時北寒林的貢獻正在增加。」
整體而言,土地碳匯在研究期間內有所增加,主要是因為北寒林吸收了更多的二氧化碳。研究發現,從1992年至2015年,陸地儲碳增加了10億噸。
下圖更深入地說明了這種差異。它顯示1992年至2015年期間,北寒林(黑線)和熱帶森林(紅線)對陸地碳匯的貢獻。上色區域表示誤差範圍。作者說,熱帶森林的不確定性較大,因為它們對影響碳損失和獲取的因素更加敏感。
1992-2015年,北寒林(黑色)和熱帶森林(紅色)對陸域碳匯的貢獻。上色區域顯示不確定性範圍。資料來源:Tagesson et al. (2020)
該圖顯示,熱帶森林很可能從陸地碳匯的最大貢獻者變為第二大,輸給了北寒林。塔格森說,下降的主要原因是熱帶地區的森林砍伐。「人為土地利用和土地覆蓋變化對熱帶森林的儲碳能力有很大的影響。」
值得一提的是,該研究僅探討到2015年,但此後,世界各地熱帶森林砍伐都在加速。去年年底,巴西亞馬遜地區的森林砍伐達到十年來新高,而中非和西非的森林砍伐也達到高點。
另一份發表在《自然永續性(Nature Sustainability)》期刊的發現,從2008年到2014年,巴西亞馬遜流域的次生林損失增加了一倍以上,導致釋放出26億噸碳(「次生林」是指近期重新生長而成的森林)。
研究發現,熱帶地區的碳損失還受到乾旱等氣象因素的影響。會導致樹木死亡並加劇野火的危險。
巴西亞馬遜流域的次生林損失增加了一倍以上,導致釋放出26億噸碳。照片來源:CIAT/NeilPalmer(CC BY-SA 2.0)
塔格森說,北寒林吸收二氧化碳速度變快的原因難以理解。研究者發現,在研究期間內,土地利用變遷和氣象因素在北寒林碳儲量的增加上僅扮演次要角色。但是,他認為二氧化碳施肥效應可能發揮了作用。植物在光合作用中使用二氧化碳,因此,隨著人類釋放出更多的二氧化碳,植物似乎生長得更快,並且儲存更多的碳。
就算在研究期間,二氧化碳施肥作用增強了北方森林吸收二氧化碳的能力,但這種作用可能會減緩甚至逆轉。未參與研究的慕尼黑科技大學地表相互作用學者安賈.拉米格(Anja Rammig)教授說:
「問題在於:這些碳能在森林中保留多久?可能很快就會流失,因為樹木長得越快,就會死得越早。如果樹木提早死亡,可能10或20年後換看到一個完全逆轉的局面。」她評論,這項新研究相當紮實地描繪出陸域碳匯變化的全貌。「因為作者研究的是地表生物量,而不僅僅是林業研究中經常使用的『綠化』效果。」
「綠化」是從上而下測量土地隨著時間的推移而變綠的程度,通常來自高解析度衛星影像。另一方面,地表生物量是覆蓋陸地表面所有活植物的質量。這考慮了所有生物量,而非上到下的估計,因此是測量森林碳匯更完整的方法。
未參與研究的波士頓大學氣候森林動態學者藍格.米尼(Ranga Myneni)教授表示:「這項研究的價值在於釐清主要生物區系對陸地碳匯的貢獻,以及這些貢獻隨時間的變化。」
Tropical forests losing ability to absorb CO2, study says by DAISY DUNNE
The world’s tropical forests are losing their ability to remove CO2 from the atmosphere, while boreal forests are absorbing emissions at an increasingly fast rate, a study finds.
The new analysis uses a combination of remote-sensing data and modelling to create a detailed picture of carbon loss and gain across all of Earth’s biomes from 1992 to 2015.
It shows a diverging picture in the world’s two most important ecosystems for storing carbon on land: tropical rainforests and “boreal” forests, which are found in the cold climate of the high latitudes.
The chief driver of carbon loss in tropical forests over the study period was deforestation. Particularly affected areas are likely to include the Amazon, Indonesia and southeastern Asia, the lead author tells Carbon Brief.
It is not fully clear what is driving carbon gains in boreal forests, another scientist tells Carbon Brief. However, one likely driver is the “CO2 fertilisation effect” – a term describing how increasing CO2 levels in the atmosphere can boost plant growth.
Overall, the findings paint a comprehensive picture of a “worrying” shift in the ability of tropical forests to absorb CO2 emissions, she adds.
Around 30% of the greenhouse gas emissions from human activity are absorbed by the land – making it an important “carbon sink”.
The land takes in CO2 from the atmosphere when trees and other types of vegetation carry out photosynthesis, the process where plants use CO2 to build new materials, such as shoots, roots and leaves. This means that, as long as plants are alive, they can act as long-term “sinks” of CO2.
The new study, published in Nature Ecology and Evolution, uses a host of techniques to create a detailed picture of carbon loss and gain from 1992 to 2015 across all of the world’s biomes, which include drylands, sparse land, tundra (an Arctic environment) and temperate, boreal and tropical regions.
(“Temperate” forests are found in moderate climates and are known for experiencing four seasons, while boreal forests are found in cooler high-latitude regions and are characterised by evergreen pines, spruces and larches.)
The map below shows the distribution of the various biomes included in the study. Temperate, boreal and tropical ecosystems are further broken down into “low” or “forest”. “Low” is used to indicate regions that are not covered by primary forest, but instead made up of grass, croplands, shrubland or savannah.

The distribution of the world’s terrestrial biomes from 1992-2015, including sparse (yellow), tundra (blue), boreal low (light green), boreal forest (dark green), temperate low (olive), temperate forest (black), drylands (orange), tropical low (turquoise), tropical forest (dark blue) and bare (grey). Source: Supplementary information, Tagesson et al. (2020)
To analyse carbon storage in each region, the authors use a combination of modelling and remote-sensing data. This data comes from satellites that use microwaves to detect changes in “above-ground biomass” – a measure of all the living plant matter that covers the land’s surface, including branches, leaves, trunks and fallen foliage.
In line with previous research, the study finds that tropical and boreal forests are the most important biomes for storing carbon. Together, these two biomes accounted for more than half (53%) the carbon held by land over the study period.
However, these two regions are now showing “divergence” in their ability to store carbon, says Dr Torben Tagesson, study lead author and researcher at Lund University in Sweden. He tells Carbon Brief:
“This study gives us an insight in how this CO2 uptake is distributed across the world – and we show that the contribution of the tropical forests is substantially decreasing. At the same time, the contribution of boreal forests is increasing.”
Overall, the land carbon sink increased over the study period – largely as a result of the boreal forests absorbing more CO2, he adds. The study finds that the land sink grew by an additional 1bn tonnes of carbon from 1992-2015.
The chart below gives a more in-depth picture of this divergence. It shows the contribution of boreal (black line) and tropical (red line) forests to the land carbon sink from 1992 to 2015.

The shaded areas show the margins of error. (The uncertainties are larger for tropical forests because they are more sensitive to individual drivers of carbon loss and gain, the authors say.)
The contribution of boreal (black) and tropical (red) forests to the land carbon sink from 1992-2015. The shadow areas show margins of uncertainty. Source: Tagesson et al. (2020)
The chart shows how tropical forests have likely gone from being the largest contributor to the land carbon sink to the second largest behind boreal forests.
The primary reason for this decline is deforestation in tropical regions, says Tagesson:
“We can clearly see that the anthropogenic land use and land cover change have a big impact for the contribution of tropical forests.”
It is worth noting that the study only looks at changes up until 2015 – and since then tropical deforestation has accelerated in many parts of the world, he adds.
Late last year deforestation of the Brazillian Amazon reached its highest level in a decade, while central and west Africa also saw a spike in forest loss.
A second study published today in Nature Sustainability finds that loss of “secondary forest” in the Brazillian Amazon more than doubled from 2008 to 2014 – causing the release of 2.6bn tonnes of carbon. (“Secondary forest” refers to forest that was replanted relatively recently.)
Carbon loss in tropical regions was also affected by “meteorological factors” such as droughts – which cause tree deaths and worsen the risk of wildfires, the study finds.
The reason why boreal forests are absorbing CO2 at an increasingly fast rate is more difficult to tease out, says Tagesson. The study finds that both land-use change and meteorological factors played “minor roles” in the observed increase in boreal carbon storage over the study period.
However, it is likely that the “CO2 fertilisation effect” is playing a role, he adds. Plants use CO2 in photosynthesis and, so, as humans emit more of it, it appears that plants are growing faster – and storing more carbon.
Though the CO2 fertilisation effect has boosted the ability of boreal forests to absorb CO2 over the study period, it is possible that this effect may slow down or even reverse, says Prof Anja Rammig, a researcher of land-surface interactions from the Technical University of Munich, who was not involved in the study. She tells Carbon Brief:
“The question is: How long will this carbon stay in forests? It could be that this carbon gets lost earlier because if trees are growing faster, they could die younger. If trees are dying younger, we could expect to see a completely reversed picture in 10 or 20 years.”
The new study is “very solid” and creates a “comprehensive picture” of how the land carbon sink is changing, she adds. “A real strength is the authors look at above-ground biomass, rather than just ‘greening’, which is often used in forestry studies.”
“Greening” is a top-down measurement of how much more green the land has become over time. It is often derived from satellites that can create high-resolution images.
On the other hand, above-ground biomass is a measure of all the living plant matter that covers the land’s surface. Because it takes into account all biomass, rather than making a top-down estimate, it can be seen as a more complete way of measuring forest carbon, she says.
The high-resolution tools used in the study make it “extremely novel”, agrees Prof Ranga Myneni, a researcher of climate-forest dynamics from Boston University, who was not involved in the research. He tells Carbon Brief:
“I think the value of this study is in being able to tease out contributions of different biomes to the land carbon sink and then look at the temporal dynamics of those contributions, principally in the case of tropical and boreal forests.”
※ 全文及圖片詳見:()
※ 本文與 行政院農業委員會 林務局 合作刊登
如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。
於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。
延伸閱讀
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
據北京市科委消息,截至2015年年底,北京共建成公用充電樁5008根,2016年將再建5000根。
日前,“電動社區”行動計畫暨北京市充電設施公共服務管理平臺(e充網)啟動,將選擇500家社區率先實現電源條件到車位,並在無充電樁安裝條件社區率先投放500台移動充電車。
北京市“電動社區”行動計畫對於三類情況不同的社區給出了三種解決方案:對於有固定停車位元且具備電容量的社區,遴選500家,率先實現電源條件到車位;對於無固定停車位的社區,鼓勵在公共管理區域率先建設公用充電樁;在無充電樁安裝條件的社區,將投放500台移動充電車,方便車主預約充電車到車位充電,其投放範圍覆蓋北京16區中包括老舊社區、保障房社區、大型居住社區等各類社區200個。
公用充電樁將實現手機支付
據e充網工作人員表示,目前e充網已經實現了北京地區建設運營商全納入,全市所有公用充電樁的位置與導航資訊均收入到了APP之中。
此外,在充電樁國標符合性改造的過程中,統一支付結算(支持支付寶、微信、銀聯)、即時資料更新、充電樁預約等功能也會同步實施,即充電樁升級一批,其即時資料、統一支付結算等功能便實現一批,到今年6月底,電動汽車車主可以從“多卡不通用”的困境中解放出來,用手機實現線上查詢支付等諸多功能。
e充網工作人員介紹,目前北京已有604根快充樁(直流樁)實現了統一支付結算、即時資料更新的功能。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益
中汽協日前預測,2016年全國電動汽車和插電式混合動力車的銷量預計將超過70萬輛,較2015年的銷量增長一倍。
2015年電動車和插電式混合動力車的合併銷量為331092輛,較2014年增長了340%。其中包括247482輛電動車和83610輛插電式混合動力車,在24萬輛多的電動汽車銷量中,包括146719輛乘用車,另有100763輛為商用車。插電式混合動力車的銷量中,60663輛為乘用車,22947輛為商用車。
根據2015年起草的藍圖,政府計畫到2020年在全國範圍內新建12000個充電站和480枚充電樁。2014年年底,全國共有780個充電站共31000枚充電樁。2015年政府還為27個省市自治區設定了電動車的最低銷量目標。
政府預計,這些措施到位後,自主品牌車企的電動車和插電式混合動力車銷量到2020年可達100萬輛,到2025年可達300萬輛。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
Spring Security的登錄驗證流程核心就是過濾器鏈。當一個請求到達時按照過濾器鏈的順序依次進行處理,通過所有過濾器鏈的驗證,就可以訪問API接口了。
SpringSecurity提供了多種登錄認證的方式,由多種Filter過濾器來實現,比如:
根據我們不同的需求實現及配置,不同的Filter會被加載到應用中。
我們就以用戶名、密碼登錄方式為例講解一下Spring Security的登錄認證流程。
該過濾器封裝用戶基本信息(用戶名、密碼),定義登錄表單數據接收相關的信息。如:
UsernamePasswordAuthenticationFilter繼承自抽象類AbstractAuthenticationProcessingFilter,該抽象類定義了驗證成功與驗證失敗的處理方法。
也就是說當我們需要自定義驗證成功或失敗的處理方法時,要去實現AuthenticationSuccessHandler或AuthenticationfailureHandler接口
ProviderManager用繼承於AuthenticationManager是登錄驗證的核心類。ProviderManager保管了多個AuthenticationProvider,用於不同類型的登錄驗證。比如:
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
……
private List<AuthenticationProvider> providers;
……
下文是ProviderManager的核心源碼,遍歷不同登錄驗證的AuthenticationProvider,只有當這種方式被支持的時候,才執行具體的登錄驗證邏輯。
public interface AuthenticationProvider {
Authentication authenticate(Authentication var1) throws AuthenticationException;
boolean supports(Class<?> var1);
}
AuthenticationProvider的實現類定義了具體的登錄驗證邏輯
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
從數據庫獲取用戶信息源碼
所以當我們需要加載用戶信息進行登錄驗證的時候,我們需要實現UserDetailsService接口,重寫loadUserByUsername方法,參數是用戶輸入的用戶名。返回值是UserDetails。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益
.NET Core具有一個承載(Hosting)系統,承載需要在後台長時間運行的服務,一個ASP.NET Core應用僅僅是該系統承載的一種服務而已。承載系統總是採用依賴注入的方式來消費它在服務承載過程所需的服務。對於承載系統來說,原始的服務註冊總是體現為一個IServiceCollection集合,最終的依賴注入容器則體現為一個IServiceProvider對象,如果要將第三方依賴注入框架整合進來,就需要利用它們解決從IServiceCollection集合到IServiceProvider對象之間的適配問題。
具體來說,我們可以在IServiceCollection集合和IServiceProvider對象之間設置一個針對某個第三方依賴注入框架的ContainerBuilder對象。我們先利用包含原始服務註冊的IServiceCollection集合來創建一個ContainerBuilder對象,再利用該對象來構建作為依賴注入容器的IServiceProvider對象。
如上圖所示的兩種轉換是利用一個IServiceProviderFactory<TContainerBuilder>對象完成的。如下面的代碼片段所示,IServiceProviderFactory<TContainerBuilder>接口定義了兩個方法,其中CreateBuilder方法利用指定的IServiceCollection集合創建出對應的ContainerBuilder對象,而CreateServiceProvider方法則進一步利用這個ContainerBuilder對象創建出作為依賴注入容器的IServiceProvider對象。
public interface IServiceProviderFactory<TContainerBuilder> { TContainerBuilder CreateBuilder(IServiceCollection services); IServiceProvider CreateServiceProvider(TContainerBuilder containerBuilder); }
.NET Core的承載系統總是利用註冊的IServiceProviderFactory<TContainerBuilder>服務來創建最終作為依賴注入容器的IServiceProvider對象。承載系統默認註冊的是如下這個DefaultServiceProviderFactory類型。如下面的代碼片段所示,DefaultServiceProviderFactory對象會直接調用指定IServiceCollection集合的BuildServiceProvider方法創建出對應的IServiceProvider對象。
public class DefaultServiceProviderFactory : IServiceProviderFactory<IServiceCollection> { public DefaultServiceProviderFactory() : this(ServiceProviderOptions.Default){} public DefaultServiceProviderFactory(ServiceProviderOptions options) =>_options = options; public IServiceCollection CreateBuilder(IServiceCollection services) => services; public IServiceProvider CreateServiceProvider( IServiceCollection containerBuilder) => containerBuilder.BuildServiceProvider(_options); }
為了讓讀者朋友對利用註冊的IServiceProviderFactory<TContainerBuilder>服務整合第三方依賴注入框架具有更加深刻的理解,我們來演示一個具體的實例。我們在《》創建了一個名為Cat的“迷你版”依賴注入框架,接下來我們將提供一個具體IServiceProviderFactory<TContainerBuilder>實現類型完成對它的整合。
我們首先創建一個名為CatBuilder的類型作為對應的ContainerBuilder。由於需要涉及針對服務範圍的創建,我們在CatBuilder類中定了如下兩個內嵌的私有類型,其中表示服務範圍的ServiceScope對象實際上就是對一個IServiceProvider對象的封裝,另一個ServiceScopeFactory類型表示創建該對象的工廠,它是對一個Cat對象的封裝。
public class CatBuilder { private class ServiceScope : IServiceScope { public ServiceScope(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider; public IServiceProvider ServiceProvider { get; } public void Dispose()=> (ServiceProvider as IDisposable)?.Dispose(); } private class ServiceScopeFactory : IServiceScopeFactory { private readonly Cat _cat; public ServiceScopeFactory(Cat cat) => _cat = cat; public IServiceScope CreateScope() => new ServiceScope(_cat); } }
一個CatBuilder對象是對一個Cat對象的封裝,它的BuildServiceProvider方法會直接返回這個Cat對象,並作為最終提供的依賴注入容器。CatBuilder在初始化過程中添加了針對IServiceScopeFactory接口的服務註冊,具體註冊的是根據作為當前子容器的Cat對象創建的ServiceScopeFactory對象。為了實現程序集範圍內的批量服務註冊,我們為CatBuilder定義了一個Register方法。
public class CatBuilder { private readonly Cat _cat; public CatBuilder(Cat cat) { _cat = cat; _cat.Register<IServiceScopeFactory>( c => new ServiceScopeFactory(c.CreateChild()), Lifetime.Transient); } public IServiceProvider BuildServiceProvider() => _cat; public CatBuilder Register(Assembly assembly) { _cat.Register(assembly); return this; } ... }
如下所示的CatServiceProviderFactory類型實現了IServiceProviderFactory<CatBuilder>接口。在實現的CreateBuilder方法中,我們創建了一個Cat對象,並將指定IServiceCollection集合包含中的服務註冊(ServiceDescriptor對象)轉換成兼容Cat的服務註冊(ServiceRegistry對象)並應用到創建的Cat對象上。我們最終利用這個Cat對象創建出返回的CatBuilder對象。實現的另一個方法CreateServiceProvider返回的是調用CatBuilder對象的CreateServiceProvider方法得到的IServiceProvider對象。
public class CatServiceProviderFactory : IServiceProviderFactory<CatBuilder> { public CatBuilder CreateBuilder(IServiceCollection services) { var cat = new Cat(); foreach (var service in services) { if (service.ImplementationFactory != null) { cat.Register(service.ServiceType, provider ) => service.ImplementationFactory(provider), service.Lifetime.AsCatLifetime()); } else if (service.ImplementationInstance != null) { cat.Register(service.ServiceType, service.ImplementationInstance); } else { cat.Register(service.ServiceType, service.ImplementationType, service.Lifetime.AsCatLifetime()); } } return new CatBuilder(cat); } public IServiceProvider CreateServiceProvider(CatBuilder containerBuilder) => containerBuilder.BuildServiceProvider(); }
Cat具有.NET Core依賴注入框架一致的服務生命周期表達方式,所以我們在將服務註冊從ServiceDescriptor類型轉化成ServiceRegistry類型時,可以實現直接完成兩種生命周期模式的轉換,具體的轉換實現在如下這個AsCatLifetime擴展方法中。
internal static class Extensions { public static Lifetime AsCatLifetime(this ServiceLifetime lifetime) { return lifetime switch { ServiceLifetime.Scoped => Lifetime.Self, ServiceLifetime.Singleton => Lifetime.Root, _ => Lifetime.Transient, }; } }
接下來我們演示如何利用CatServiceProviderFactory來創建作為依賴注入容器的IServiceProvider對象。我們定義了如下的接口和對應的實現類型,其中Foo、Bar、Baz和Qux類型分別實現了對應的接口IFoo、IBar、IBaz和IQux,其中Qux類型上標註了一個MapToAttribute特性註冊了與對應接口IQux之間的映射。為了反映Cat對服務實例生命周期的控制,我們讓它們派生於同一個基類Base。Base實現了IDisposable接口,我們在其構造函數和實現的Dispose方法中輸出相應的文本以確定對應的實例何時被創建和釋放。
public interface IFoo {} public interface IBar {} public interface IBaz {} public interface IQux {} public interface IFoobar<T1, T2> {} public class Base : IDisposable { public Base() => Console.WriteLine($"Instance of {GetType().Name} is created."); public void Dispose() => Console.WriteLine($"Instance of {GetType().Name} is disposed."); } public class Foo : Base, IFoo{ } public class Bar : Base, IBar{ } public class Baz : Base, IBaz{ } [MapTo(typeof(IQux), Lifetime.Root)] public class Qux : Base, IQux { } public class Foobar<T1, T2>: IFoobar<T1,T2> { public IFoo Foo { get; } public IBar Bar { get; } public Foobar(IFoo foo, IBar bar) { Foo = foo; Bar = bar; } }
在如下所示的演示程序中,我們創建了一個ServiceCollection集合,並採用三種不同的生命周期模式分別添加了針對IFoo、IBar和IBaz接口的服務註冊。我們接下來根據這個ServiceCollection集合創建了一個CatServiceProviderFactory對象,並調用其CreateBuilder方法創建出對應的CatBuilder對象。我們隨後調用了CatBuilder對象的Register方法完成了針對當前入口程序集的批量服務註冊,其目的在於添加針對IQux/Qux的服務註冊。
class Program { static void Main() { var services = new ServiceCollection() .AddTransient<IFoo, Foo>() .AddScoped<IBar>(_ => new Bar()) .AddSingleton<IBaz>(new Baz()); var factory = new CatServiceProviderFactory(); var builder = factory.CreateBuilder(services) .Register(Assembly.GetEntryAssembly()); var container = factory.CreateServiceProvider(builder); GetServices(); GetServices(); Console.WriteLine("\nRoot container is disposed."); (container as IDisposable)?.Dispose(); void GetServices() { using (var scope = container.CreateScope()) { Console.WriteLine("\nService scope is created."); var child = scope.ServiceProvider; child.GetService<IFoo>(); child.GetService<IBar>(); child.GetService<IBaz>(); child.GetService<IQux>(); child.GetService<IFoo>(); child.GetService<IBar>(); child.GetService<IBaz>(); child.GetService<IQux>(); Console.WriteLine("\nService scope is disposed."); } } } }
在調用CatServiceProviderFactory對象的CreateServiceProvider方法創建出作為依賴注入容器的IServiceProvider對象之後,我們先後兩次調用了本地方法GetServices方法。GetServices方法會利用這個IServiceProvider對象創建一個服務範圍,並利用此服務範圍內的IServiceProvider提供兩組服務實例。通過CatServiceProviderFactory創建的IServiceProvider對象在最終通過調用其Dispose方法進行釋放。該程序運行之後會在控制台上輸出如圖4-16所示的結果,輸出結果體現的服務生命周期與演示程序體現的是完全一致的。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!
GIMP源代碼鏈接:
GEGL相關代碼鏈接:
最近因為要研究下色溫算法,順便下載了最新的GIMP軟件,色溫算法倒是找到了(有空單獨來講下),也順便看看GIMP都有些什麼更新,嗯,更新還是蠻多的,界面UI上有很多改動,有些已經改的面目全非了。隨便瞄了一下Enhance菜單,發現裏面有一個Nosie Reduction算法,試了下,還有點效果。於是在github上下載了GIMP的源代碼,可是在源代碼里搜索相關的關鍵詞確沒有發現任何的相關代碼,後來才發現很多東西都有個GEGL關鍵詞,結果一百度,原來他是一個單獨的軟件包,於是有下載了GEGL的源代碼,終於在gegl-master\operations\common\裏面看到了noise-reduction.c文件。
其核心的代碼如下:
static void noise_reduction (float *src_buf, /* source buffer, one pixel to the left and up from the starting pixel */ int src_stride, /* stridewidth of buffer in pixels */ float *dst_buf, /* destination buffer */ int dst_width, /* width to render */ int dst_height, /* height to render */ int dst_stride) /* stride of target buffer */ { int c; int x,y; int dst_offset; #define NEIGHBOURS 8 #define AXES (NEIGHBOURS/2) #define POW2(a) ((a)*(a)) /* core code/formulas to be tweaked for the tuning the implementation */ #define GEN_METRIC(before, center, after) \ POW2((center) * 2 - (before) - (after)) /* Condition used to bail diffusion from a direction */ #define BAIL_CONDITION(new,original) ((new) > (original)) #define SYMMETRY(a) (NEIGHBOURS - (a) - 1) /* point-symmetric neighbour pixel */ #define O(u,v) (((u)+((v) * src_stride)) * 4) int offsets[NEIGHBOURS] = { /* array of the relative distance i float * pointers to each of neighbours * in source buffer, allows quick referencing. */ O( -1, -1), O(0, -1), O(1, -1), O( -1, 0), O(1, 0), O( -1, 1), O(0, 1), O(1, 1)}; #undef O dst_offset = 0; for (y=0; y<dst_height; y++) { float *center_pix = src_buf + ((y+1) * src_stride + 1) * 4; dst_offset = dst_stride * y; for (x=0; x<dst_width; x++) { for (c=0; c<3; c++) /* do each color component individually */ { float metric_reference[AXES]; int axis; int direction; float sum; int count; for (axis = 0; axis < AXES; axis++) { /* initialize original metrics for the horizontal, vertical and 2 diagonal metrics */ float *before_pix = center_pix + offsets[axis]; float *after_pix = center_pix + offsets[SYMMETRY(axis)]; metric_reference[axis] = GEN_METRIC (before_pix[c], center_pix[c], after_pix[c]); } sum = center_pix[c]; count = 1; /* try smearing in data from all neighbours */ for (direction = 0; direction < NEIGHBOURS; direction++) { float *pix = center_pix + offsets[direction]; float value = pix[c] * 0.5 + center_pix[c] * 0.5; int axis; int valid; /* check if the non-smoothing operating check is true if * smearing from this direction for any of the axes */ valid = 1; /* assume it will be valid */ for (axis = 0; axis < AXES; axis++) { float *before_pix = center_pix + offsets[axis]; float *after_pix = center_pix + offsets[SYMMETRY(axis)]; float metric_new = GEN_METRIC (before_pix[c], value, after_pix[c]); if (BAIL_CONDITION(metric_new, metric_reference[axis])) { valid = 0; /* mark as not a valid smoothing, and .. */ break; /* .. break out of loop */ } } if (valid) /* we were still smooth in all axes */ { /* add up contribution to final result */ sum += value; count ++; } } dst_buf[dst_offset*4+c] = sum / count; } dst_buf[dst_offset*4+3] = center_pix[3]; /* copy alpha unmodified */ dst_offset++; center_pix += 4; } } }
這個代碼看上去比較混亂,沒辦法,大型軟件沒有哪一個代碼看上去能讓人省心的,而且也不怎麼講究效率,我測試了一個3K*2K的彩色圖,在GIMP里大概在4S左右處理完成,屬於很慢的了,看這個代碼,也知道大概有width * height * 3 * 8 * 4 * Iter個循環,計算量確實是相當的大。
我試着嘗試優化這個算法。
優化的第一步是弄明白算法的原理,在GIMP的UI界面上可當鼠標停留在Noise Reduction菜單上時,會出現Anisotroic smoothing operation字樣,所以初步分析他是屬於各項異性擴散類的算法。稍微分析下代碼,也確實是。明顯這屬於一個領域濾波器,對每一個像素,求取其3*3領域內的累加值,但是3*3領域的權重並不是平均分佈或者是高斯分佈,而是和領域的值有關的,如果領域的值是一個邊緣點,他將不參与到累加中,權重為0,否則權重為1。
具體一點,對於領域里的任何一點,我們先求取其和中心點的平均值,對應 float value = pix[c] * 0.5 + center_pix[c] * 0.5; 這條語句,然後計算這個值在2個45度對角線及水平和垂直方向的梯度(4個梯度值)是否比中心點在四個方向的梯度都小,如果都小,說明這個領域點不屬於邊緣點,可以往這個方向擴散,把他計入到統計值中,如果有任何一個方向小了,則不參与最終的計算。
上面的過程可以看成是標準的各項異性擴散的特殊在特殊處理,他具有各項異性擴散的特性,也具有一些特殊性。
下一步,稍微分析下最簡單的優化方法。第一,我們知道,在大循環里一般不建議嵌套入小的循環,這樣是很低效的。我們觀察到上面代碼里的
for (c=0; c<3; c++) /* do each color component individually */
這個語句主要是為了方便表達3通道的處理的方便,但是其實三通道之間的處理時沒有任何聯繫的,對於這樣的算法,很明顯,我們可以一次性當然處理R G B R G B R G B ,而不需要像GIMP這個代碼這樣按照 RRR GGG BBB這樣的順序來寫,GIMP這種寫法浪費了很多CPU的CACHE,畢竟R和G和B在內存類分佈本來就是連續的。這樣就減少了一個小循環。
第二個優化的點是,對於普通的圖像數據,我們可以考慮不用浮點數來處理,畢竟上述計算里只有*0.5這樣的浮點操作,我們考慮將原先的圖像數據放大一定的倍數,然後用整形來玩,在處理完后,在縮小到原來的範圍,比如使用short類型應該就足夠了,我把數據放大16倍或者32倍,甚至8倍應該都能獲得足夠的精度。
第三個優化點,程序中是使用的Pow來判斷梯度的大小的,其實可以不用,直接使用絕對值的結果和Pow是完全一樣的,而絕對值的計算量比pow要小很多,對於整數則更為如此(還可以不考慮pow數據類型的改變,比如short的絕對值還是short類型,但是其pow可能就需要用int來表示了,這在SIMD優化會產生不同的結果)。
第四個優化點是 for (axis = 0; axis < AXES; axis++)這個小循環我們應該把它直接展開。
第五點,我們還可以考慮我在其他文章里提到的支持Inplace操作的方式,這樣noise_reduction這個函數的輸入和輸出就可以是同一個內存。
第六點還有小點上的算法改進,比如一些中間計算沒必要重複進行,有些可以提到外部來等。
綜合上面的描述,我整理除了一個優化的C語言版本的程序,如下所示:
void IM_AnisotropicDiffusion3X3(short *Src, short *Dest, int Width, int Height, int SplitPos, int Stride) { int Channel = Stride / Width; short *RowCopy = (short *)malloc((Width + 2) * 3 * Channel * sizeof(short)); short *First = RowCopy; short *Second = RowCopy + (Width + 2) * Channel; short *Third = RowCopy + (Width + 2) * 2 * Channel; memcpy(Second, Src, Channel * sizeof(short)); memcpy(Second + Channel, Src, Width * Channel * sizeof(short)); // 拷貝數據到中間位置 memcpy(Second + (Width + 1) * Channel, Src + (Width - 1) * Channel, Channel * sizeof(short)); memcpy(First, Second, (Width + 2) * Channel * sizeof(short)); // 第一行和第二行一樣 memcpy(Third, Src + Stride, Channel * sizeof(short)); // 拷貝第二行數據 memcpy(Third + Channel, Src + Stride, Width * Channel* sizeof(short)); memcpy(Third + (Width + 1) * Channel, Src + Stride + (Width - 1) * Channel, Channel* sizeof(short)); for (int Y = 0; Y < Height; Y++) { short *LinePD = Dest + Y * Stride; if (Y != 0) { short *Temp = First; First = Second; Second = Third; Third = Temp; } if (Y == Height - 1) { memcpy(Third, Second, (Width + 2) * Channel * sizeof(short)); } else { memcpy(Third, Src + (Y + 1) * Stride, Channel * sizeof(short)); memcpy(Third + Channel, Src + (Y + 1) * Stride, Width * Channel * sizeof(short)); // 由於備份了前面一行的數據,這裏即使Src和Dest相同也是沒有問題的 memcpy(Third + (Width + 1) * Channel, Src + (Y + 1) * Stride + (Width - 1) * Channel, Channel * sizeof(short)); } for (int X = 0; X < SplitPos * Channel; X++) { short LT = First[X], T = First[X + Channel], RT = First[X + 2 * Channel]; short L = Second[X], C = Second[X + Channel], R = Second[X + 2 * Channel]; short LB = Third[X], B = Third[X + Channel], RB = Third[X + 2 * Channel]; short LT_RB = LT + RB, RT_LB = RT + LB; short T_B = T + B, L_R = L + R, C_C = C + C; short Dist1 = IM_Abs(C_C - LT_RB), Dist2 = IM_Abs(C_C - T_B); short Dist3 = IM_Abs(C_C - RT_LB), Dist4 = IM_Abs(C_C - L_R); int Sum = C_C, Amount = 2; short LT_C = LT + C; if ((IM_Abs(LT_C - LT_RB) < Dist1) && (IM_Abs(LT_C - T_B) < Dist2) && (IM_Abs(LT_C - RT_LB) < Dist3) && (IM_Abs(LT_C - L_R) < Dist4)) { Sum += LT_C; Amount += 2; } short T_C = T + C; if ((IM_Abs(T_C - LT_RB) < Dist1) && (IM_Abs(T_C - T_B) < Dist2) && (IM_Abs(T_C - RT_LB) < Dist3) && (IM_Abs(T_C - L_R) < Dist4)) { Sum += T_C; Amount += 2; } short RT_C = RT + C; if ((IM_Abs(RT_C - LT_RB) < Dist1) && (IM_Abs(RT_C - T_B) < Dist2) && (IM_Abs(RT_C - RT_LB) < Dist3) && (IM_Abs(RT_C - L_R) < Dist4)) { Sum += RT_C; Amount += 2; } short L_C = L + C; if ((IM_Abs(L_C - LT_RB) < Dist1) && (IM_Abs(L_C - T_B) < Dist2) && (IM_Abs(L_C - RT_LB) < Dist3) && (IM_Abs(L_C - L_R) < Dist4)) { Sum += L_C; Amount += 2; } short R_C = R + C; if ((IM_Abs(R_C - LT_RB) < Dist1) && (IM_Abs(R_C - T_B) < Dist2) && (IM_Abs(R_C - RT_LB) < Dist3) && (IM_Abs(R_C - L_R) < Dist4)) { Sum += R_C; Amount += 2; } short LB_C = LB + C; if ((IM_Abs(LB_C - LT_RB) < Dist1) && (IM_Abs(LB_C - T_B) < Dist2) && (IM_Abs(LB_C - RT_LB) < Dist3) && (IM_Abs(LB_C - L_R) < Dist4)) { Sum += LB_C; Amount += 2; } short B_C = B + C; if ((IM_Abs(B_C - LT_RB) < Dist1) && (IM_Abs(B_C - T_B) < Dist2) && (IM_Abs(B_C - RT_LB) < Dist3) && (IM_Abs(B_C - L_R) < Dist4)) { Sum += B_C; Amount += 2; } short RB_C = RB + C; if ((IM_Abs(RB_C - LT_RB) < Dist1) && (IM_Abs(RB_C - T_B) < Dist2) && (IM_Abs(RB_C - RT_LB) < Dist3) && (IM_Abs(RB_C - L_R) < Dist4)) { Sum += RB_C; Amount += 2; } LinePD[X] = Sum / Amount; } } free(RowCopy); }
調用函數
int IM_ReduceNoise(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride, int SplitPos, int Strength) { int Channel = Stride / Width; if ((Src == NULL) || (Dest == NULL)) return IM_STATUS_NULLREFRENCE; if ((Width <= 0) || (Height <= 0)) return IM_STATUS_INVALIDPARAMETER; if ((Channel != 1) && (Channel != 3)) return IM_STATUS_INVALIDPARAMETER; Strength = IM_ClampI(Strength, 1, 10); SplitPos = IM_ClampI(SplitPos, 0, Width); int Status = IM_STATUS_OK; short *Temp = (short *)malloc(Height * Stride * sizeof(short)); if (Temp == NULL) return IM_STATUS_OUTOFMEMORY; for (int Y = 0; Y < Height * Stride; Y++) { Temp[Y] = Src[Y] << 3; } for (int Y = 0; Y < Strength; Y++) { IM_AnisotropicDiffusion3X3(Temp, Temp, Width, Height, SplitPos, Stride); } for (int Y = 0; Y < Height * Stride; Y++) { Dest[Y] = Temp[Y] >> 3; } free(Temp); return IM_STATUS_OK; }
是不是看起來比上面的GIMP得要舒服些,而且中間也大概只要原始圖像2倍的一個臨時內存了。在速度和內存佔用方面都前進了很多。
我測試前面提到的那副3K*2K的圖像,耗時要7S多,但是我測試表面GIMP用了多核的,如果論單核,我這裏的速度要比他快2倍多。
很明顯,這個速度是不可以接受的,我們需要繼續優化。
我還是老套路,使用SIMD指令做處理,看到上面的代碼,其實真的覺得好容易改成SIMD的。
short LT_RB = LT + RB, RT_LB = RT + LB;
short T_B = T + B, L_R = L + R, C_C = C + C;
short Dist1 = IM_Abs(C_C - LT_RB), Dist2 = IM_Abs(C_C - T_B);
short Dist3 = IM_Abs(C_C - RT_LB), Dist4 = IM_Abs(C_C - L_R);
這些加減絕對值都有完全對應的SSE指令。 _mm_add_epi16、 _mm_sub_epi16、_mm_abs_epi16,基本上就是照着寫。
稍微複雜一點就是這裏:
if ((IM_Abs(LT_C - LT_RB) < Dist1) && (IM_Abs(LT_C - T_B) < Dist2) && (IM_Abs(LT_C - RT_LB) < Dist3) && (IM_Abs(LT_C - L_R) < Dist4))
{
Sum += LT_C;
Amount += 2;
}
在C語言里,這裏判斷會進行短路計算,即如果前一個條件已經不滿足了,後續的計算就不會進行。但是在SIMD指令里,是沒有這樣的機制的。我們只能全部計算,然後在通過某一種條件組合。
在合理,要實現符合條件就進行累加,不符合條件就不做處理的需求,我們需要稍作修改,即不符合條件不是不做處理,而是加0,加0對結果沒有影響的。主要藉助下面的_mm_blendv_epi8來實現。
__m128i LT_C = _mm_add_epi16(LT, C); Flag1 = _mm_cmplt_epi16(_mm_abs_epi16(_mm_sub_epi16(LT_C, LT_RB)), Dist1); // 只能全部都計算,但還是能提速 Flag2 = _mm_cmplt_epi16(_mm_abs_epi16(_mm_sub_epi16(LT_C, T_B)), Dist2); Flag3 = _mm_cmplt_epi16(_mm_abs_epi16(_mm_sub_epi16(LT_C, RT_LB)), Dist3); Flag4 = _mm_cmplt_epi16(_mm_abs_epi16(_mm_sub_epi16(LT_C, L_R)), Dist4); Flag = _mm_and_si128(_mm_and_si128(Flag1, Flag2), _mm_and_si128(Flag3, Flag4)); Sum = _mm_adds_epu16(Sum, _mm_blendv_epi8(Zero, LT_C, Flag)); Amount = _mm_adds_epu16(Amount, _mm_blendv_epi8(Zero, Two, Flag));
注意到我們這裏用到了_mm_adds_epu16,無符號的16位加法,這是因為我需要盡量的提速,因此需要減少類型轉換的次數。同時,我們看到在統計累加值時,我們並沒有求平均值,而是直接用的累加值,這樣理論上最大的累加值就是 255 * n * (8 + 1) * 2 < 65535, 這樣n最大能取15,但是15不是個好數據,在放大和縮小都不能用移位來實現,所以我最後取得放大係數為8。
另外,在最後還有個16位的整數的除法問題,這個沒有辦法,SSE指令沒有提供整數的除法計算方法,還只能轉換到浮點后,再次轉換回來。
這樣用SSE處理后,還是同一幅測試圖像,在同一台PC上速度能提升到400ms(4次迭代),比之前的普通的C語言提高了約17倍的速度。
在現代CPU中,具有AVX2指令集已經是很普遍的了,單AVX2能同時處理256字節的數據,比SSE還要多一倍,我也常使用AVX2進行優化處理,速度能達到250ms,相當於普通C語言的28倍之多(但是AVX編程里有很多坑,這些坑都拜AVX不是完全的按照SSE的線性擴展導致的,這個後續有時間我單獨提出)。
經過測試,1080P的圖像使用4次迭代大約需要80ms,3次迭代55ms,2次迭代月40ms,也就是說前面的一些方法和縮小所使用的時間幾乎可以忽略。
選了幾幅有特點的圖進行了去燥測試,其中分界線左側的位處理的效果,右側為未處理的。
但是,這個算法也還是不是很好,他對於圖像容易出現輕微的油畫效果,對於一些細節特別豐富的圖像非常明顯,比如下圖:
這個應該是不太可以接受的,也許可以通過修改部分權重的規則來改變這個現象。這個屬於後期研究的問題了。
另外,在GIMP里也提供了這個算法的OPENCL實現,有興趣的可以源代碼里找一找,不曉得速度怎麼樣。
本文Demo下載地址: ,見其中的Denoise -> Anisotroic Diffusion 菜單。
寫博不易,歡迎土豪打賞讚助。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益