【10】大恒相機SDK C++開發 ——對相機采集的原圖像數據IFrameData裁剪ROI 實時顯示在pictureBox中,3種方法實現(效率不同)

文章目錄

  • 1 在回調函數中實現
  • 2 獨立封裝調用
    • 2.1 獲取圖像寬、高、pBuffer、channel
    • 2.2 內存圖像數據截取ROI并顯示
    • 2.3 回調函數調用
  • 3 for循環嵌套 方法2
  • 4 for循環嵌套 方法3
  • 5 按行復制數據提高效率,但很耗內存
  • 6 unsafe代碼 解釋及注意事項 看我另一篇文章
  • 7 ConvertToRGB24詳細解釋 、示例、注意事項 看我另一篇文章
  • 8 問題與反思
    • 8.1 被反復創建和使用,需手動釋放嗎?
    • 8.2 創建一個全局Bitmap bitma,多線程訪問會沖突嗎?

當我們只要顯示ROI區域的畫面(顯示局部細節),而不是相機整個畫面時,就需要對圖像數據進行截取。

對于工業相機原圖像數據IFrameData 裁剪ROI,并不像 OpenCV中截取ROI那么簡單, 下面提供3種方法實現(效率不同),并將ROI畫面實時顯示在pictureBox中,

1 在回調函數中實現

public IntPtr pBuffer = IntPtr.Zero;private void __OnFrameCallbackFun(object objUserParam, IFrameData objIFrameData)
{int channel = 0;try{if (null != objIFrameData){//************************************************************//獲取圖像信息,為圖像處理等功能做準備//*************************************************************int imgHeight = (int)objIFrameData.GetHeight();int imgWidth = (int)objIFrameData.GetWidth();//************************************************************// 對圖像進行裁剪并顯示在 PictureBox 中//*************************************************************Rectangle ROI = new Rectangle(1323, 212, 2200, 1500);//獲取圖像bufferGX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;emValidBits = __GetBestValudBit(objIFrameData.GetPixelFormat());// 判斷圖像是否成功接收if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIFrameData.GetStatus()){// 在關鍵部分的代碼前加鎖lock (this){if (m_bIsColor){// 彩色圖像pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, false);//最后一個參數是否垂直翻轉圖像channel = 3;}else{// 黑白圖像if (__IsPixelFormat8(objIFrameData.GetPixelFormat())){pBuffer = objIFrameData.GetBuffer();}else{pBuffer = objIFrameData.ConvertToRaw8(emValidBits);}channel = 1;}PixelFormat format = new PixelFormat();format = channel == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed;//若3通道彩色圖像,否則黑白圖像//創建ROI 空圖像Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);int srcStride = imgWidth * channel;// 原圖每行數據長度int destStride = bmpData.Stride;// 新圖每行數據長度int srcStartX = (ROI.X - 1) * channel; // ROI 起始 X 坐標在原圖數據中的位置(二維視角)int srcStartY = ROI.Y - 1; // ROI 起始 Y 坐標在原圖數據中的位置(二維視角)int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原圖數據中的偏移量(原圖數據在內存中是一行,一維數組)unsafe{byte* srcPtr = (byte*)pBuffer + srcOffset;byte* destPtr = (byte*)bmpData.Scan0;for (int y = 0; y < ROI.Height; y++){for (int x = 0; x < ROI.Width * channel; x++){destPtr[x] = srcPtr[x];}srcPtr += srcStride;destPtr += destStride;}}bitmap.UnlockBits(bmpData);// 在這里處理 bitmap 圖像,下面是顯示舉例m_bitmap = bitmap;//將其賦值給全局的m_bitmap,從而可以在外部調用ShowProcessedImage(m_nOperateID, bitmap);//傳給函數,進行處理顯示,//pictureBox1.Image = bitmap;//傳給pictureBox1直接顯示}}}m_objCFps.IncreaseFrameNum();}catch (Exception ex){MessageBox.Show(ex.Message);}
}

2 獨立封裝調用

2.1 獲取圖像寬、高、pBuffer、channel

        private void getImgInfo(IFrameData objIFrameData, ref IntPtr pBuffer){try{if (null != objIFrameData){//獲取圖像寬高SrcImgHeight = (int)objIFrameData.GetHeight();SrcImgWidth = (int)objIFrameData.GetWidth();//獲取圖像bufferGX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;emValidBits = __GetBestValudBit(objIFrameData.GetPixelFormat());// 判斷圖像是否成功接收if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIFrameData.GetStatus()){// 在關鍵部分的代碼前加鎖lock (this){if (colorFlag){// 彩色圖像pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, false);//最后一個參數是否垂直翻轉圖像,true則翻轉channel = 3;}else{// 黑白圖像if (__IsPixelFormat8(objIFrameData.GetPixelFormat())){pBuffer = objIFrameData.GetBuffer();}else{pBuffer = objIFrameData.ConvertToRaw8(emValidBits);}channel = 1;}}}}}catch (Exception ex){MessageBox.Show(ex.Message);}}

2.2 內存圖像數據截取ROI并顯示

          private void getRoiBmpData(Rectangle ROI, IntPtr pBuffer, ref Bitmap ROI_bitmap){try{if (null != pBuffer){ //判斷像素格式PixelFormat format = new PixelFormat();// format = channels == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed;//若3通道彩色圖像,否則黑白圖像switch (channel){case 1:format = PixelFormat.Format8bppIndexed;break;case 3:format = PixelFormat.Format24bppRgb;break;case 4:format = PixelFormat.Format32bppArgb;break;}// 在關鍵部分的代碼前加鎖lock (this){//創建ROI 空圖像Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);int srcStride = SrcImgWidth * channel;// 原圖每行數據長度int destStride = bmpData.Stride;// 新圖每行數據長度int srcStartX = (ROI.X - 1) * channel; // ROI 起始 X 坐標在原圖數據中的位置(二維視角)int srcStartY = ROI.Y - 1; // ROI 起始 Y 坐標在原圖數據中的位置(二維視角)int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原圖數據中的偏移量(原圖數據在內存中是一行,一維數組)unsafe{byte* srcPtr = (byte*)pBuffer + srcOffset;byte* destPtr = (byte*)bmpData.Scan0;for (int y = 0; y < ROI.Height; y++){for (int x = 0; x < ROI.Width * channel; x++){destPtr[x] = srcPtr[x];}srcPtr += srcStride;destPtr += destStride;}}bitmap.UnlockBits(bmpData);// 在這里處理 bitmap 圖像//ShowProcessedImage(m_nOperateID, bitmap);ROI_bitmap = bitmap;}}}catch (Exception ex){MessageBox.Show(ex.Message);}}

2.3 回調函數調用

public static int SrcImgHeight, SrcImgWidth;int channel = 0;bool isShowSrcImg = true;bool camImg_isProcess = false; //是否圖像處理,不執行圖像處理的時候,默認開啟預覽模式。bool isPreviewRoiImg = false; //是否圖像處理,不執行圖像處理的時候,默認開啟預覽模式。Rectangle ROI = new Rectangle(1323, 212, 2200, 1500);Bitmap bitmapB6;

代碼中未出現的變量為 全局變量,或者庫中的變量

        private void __OnFrameCallbackFun_1(object objUserParam, IFrameData objIFrameData){try{if (isShowSrcImg) //當開始處理圖像時原視頻要暫停,否則buffer中的數據會變化,圖像上下顛倒交替出現(因Show(objIFrameData)中顯示實現對圖像數據做了垂直翻轉){//************************************************************//顯示相機獲取的原圖//************************************************************                 m_objGxBitmap1.Show(objIFrameData);}//獲取圖像寬、高、pBuffer、channel等信息getImgInfo2(objIFrameData, ref pBuffer1);//************************************************************// 對圖像進行裁剪并顯示在 PictureBox 中//*************************************************************if (isPreviewRoiImg)// 開啟預覽模式。{getRoiBmpData2(ROI, pBuffer1, ref bitmapB6);//ImageShow1.Image = bitmapB6;}//統計幀率m_objCFps1.IncreaseFrameNum();}catch (Exception ex){MessageBox.Show("回調函數2" + ex.Message);}}

3 for循環嵌套 方法2

        private void __OnFrameCallbackFun(object objUserParam, IFrameData objIFrameData){try{if (null != objIFrameData){// 獲取圖像信息int srcImgWidth = (int)objIFrameData.GetWidth();int srcImgHeight = (int)objIFrameData.GetHeight();GX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;emValidBits = __GetBestValudBit(objIFrameData.GetPixelFormat());// 判斷圖像是否成功接收if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIFrameData.GetStatus()){// 在關鍵部分的代碼前加鎖lock (this){IntPtr pBuffer = IntPtr.Zero;if (m_bIsColor){// 彩色圖像pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, false);}else{// 黑白圖像if (__IsPixelFormat8(objIFrameData.GetPixelFormat())){pBuffer = objIFrameData.GetBuffer();}else{pBuffer = objIFrameData.ConvertToRaw8(emValidBits);}}// 創建ROI BitmapBitmap bitmap = new Bitmap(ROI.Width, ROI.Height, PixelFormat.Format24bppRgb);BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);// 計算每行字節數int stride = bmpData.Stride;int bytes = Math.Abs(stride) * bitmap.Height;// 創建一個 byte 數組來存儲圖像數據byte[] imageData = new byte[bytes];// 計算ROI在原始圖像中的偏移位置int roiOffsetX = ROI.X;int roiOffsetY = ROI.Y;// 將 pBuffer 中的圖像數據復制到數組中,考慮ROI在原始圖像中的偏移位置for (int y = 0; y < ROI.Height; y++){int srcY = roiOffsetY + y;int srcIndex = srcY * srcImgWidth * 3; // 假設是24位RGB圖像for (int x = 0; x < ROI.Width; x++){int srcX = roiOffsetX + x;int dstIndex = (y * ROI.Width + x) * 3;imageData[dstIndex] = Marshal.ReadByte(pBuffer, srcIndex + srcX * 3);imageData[dstIndex + 1] = Marshal.ReadByte(pBuffer, srcIndex + srcX * 3 + 1);imageData[dstIndex + 2] = Marshal.ReadByte(pBuffer, srcIndex + srcX * 3 + 2);}}// 復制圖像數據到 Bitmap 中Marshal.Copy(imageData, 0, bmpData.Scan0, bytes);bitmap.UnlockBits(bmpData);// 顯示圖像ShowProcessedImage(m_nOperateID, bitmap);}}}m_objCFps.IncreaseFrameNum();}catch (Exception ex){MessageBox.Show(ex.Message);}}

4 for循環嵌套 方法3

        private void __OnFrameCallbackFun(object objUserParam, IFrameData objIFrameData){try{if (null != objIFrameData){// 獲取圖像信息SrcImgWidth = (int)objIFrameData.GetWidth();SrcImgHeight = (int)objIFrameData.GetHeight();GX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;emValidBits = __GetBestValudBit(objIFrameData.GetPixelFormat());PixelFormat format;// 判斷圖像是否成功接收if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIFrameData.GetStatus()){// 在關鍵部分的代碼前加鎖lock (this){IntPtr pBuffer = IntPtr.Zero;int channel;if (m_bIsColor){// 彩色圖像pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, false); // 最后一個參數是否垂直翻轉圖像,true則翻轉channel = 3;}else{// 黑白圖像if (__IsPixelFormat8(objIFrameData.GetPixelFormat())){pBuffer = objIFrameData.GetBuffer();}else{pBuffer = objIFrameData.ConvertToRaw8(emValidBits);}channel = 1;}// 判斷像素格式format = channel == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed;// 創建ROI 空圖像m_bitmap = new Bitmap(ROI.Width, ROI.Height, format);BitmapData bmpData = m_bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, m_bitmap.PixelFormat);int srcStride = SrcImgWidth * channel; // 原圖每行數據長度int destStride = bmpData.Stride;       // 新圖每行數據長度int srcStartX = (ROI.X - 1) * channel; // ROI 起始 X 坐標在原圖數據中的位置(二維視角)int srcStartY = ROI.Y - 1;             // ROI 起始 Y 坐標在原圖數據中的位置(二維視角)int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原圖數據中的偏移量(原圖數據在內存中是一行,一維數組)// 將 pBuffer 中的圖像數據復制到數組中,考慮ROI在原始圖像中的偏移位置for (int y = 0; y < ROI.Height; y++){int srcY = srcStartY + y;int srcIndex = srcOffset + srcY * srcStride;for (int x = 0; x < ROI.Width * channel; x++){int srcX = srcStartX + x;int dstIndex = y * destStride + x;// 復制像素數據Marshal.WriteByte(bmpData.Scan0, dstIndex, Marshal.ReadByte(pBuffer, srcIndex + x));}}m_bitmap.UnlockBits(bmpData);// 顯示圖像ShowProcessedImage(m_nOperateID, m_bitmap);}}m_objCFps.IncreaseFrameNum();}}catch (Exception ex){MessageBox.Show(ex.Message);}}

5 按行復制數據提高效率,但很耗內存

        private unsafe void __OnFrameCallbackFun111(object objUserParam, IFrameData objIFrameData){try{if (null != objIFrameData){// 獲取圖像信息SrcImgWidth = (int)objIFrameData.GetWidth();SrcImgHeight = (int)objIFrameData.GetHeight();GX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;emValidBits = __GetBestValudBit(objIFrameData.GetPixelFormat());PixelFormat format;// 判斷圖像是否成功接收if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIFrameData.GetStatus()){// 在關鍵部分的代碼前加鎖lock (this){if (m_bIsColor){// 彩色圖像pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, false); // 最后一個參數是否垂直翻轉圖像,true則翻轉channel = 3;}else{// 黑白圖像if (__IsPixelFormat8(objIFrameData.GetPixelFormat())){pBuffer = objIFrameData.GetBuffer();}else{pBuffer = objIFrameData.ConvertToRaw8(emValidBits);}channel = 1;}// 判斷像素格式format = channel == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed;// 創建ROI 空圖像m_bitmap = new Bitmap(ROI.Width, ROI.Height, format);BitmapData bmpData = m_bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, m_bitmap.PixelFormat);int srcStride = SrcImgWidth * channel; // 原圖每行數據長度  int destStride = bmpData.Stride;// 新圖每行數據長度int srcStartX = (ROI.X - 1) * channel; // ROI 起始 X 坐標在原圖數據中的位置(二維視角)      int srcStartY = ROI.Y - 1; // ROI 起始 Y 坐標在原圖數據中的位置(二維視角)// ROI 起始位置在原圖數據中的偏移量(原圖數據在內存中是一行,一維數組)int srcOffset = srcStartY * srcStride + srcStartX;unsafe{// 計算 ROI 區域總字節數//int bytes = Math.Abs(destStride) * m_bitmap.Height;byte* bmpPtr = (byte*)bmpData.Scan0; // 指向 ROI Bitmap 的掃描行指針                        byte* srcPtr = (byte*)pBuffer + srcOffset;    // 指向原始圖像數據的指針                         for (int y = 0; y < ROI.Height; y++)  // 逐行復制ROI區域的圖像數據{// 復制當前行的圖像數據到ROI Bitmap中//從源地址 srcPtr開始,每次復制destStride個字節(一整行的數據)到目標地址 bmpPtr 處,第四個參數偏移量Buffer.MemoryCopy(srcPtr, bmpPtr, destStride, destStride);bmpPtr += destStride;srcPtr += srcStride;}}m_bitmap.UnlockBits(bmpData);// 顯示圖像ShowProcessedImage(m_nOperateID, m_bitmap);}}m_objCFps.IncreaseFrameNum();}}catch (Exception ex){MessageBox.Show(ex.Message);}}

6 unsafe代碼 解釋及注意事項 看我另一篇文章

7 ConvertToRGB24詳細解釋 、示例、注意事項 看我另一篇文章

8 問題與反思

C# 自定義函數getRoiBmpData,該函數功能“對相機獲取到的 IFrameData 轉換為pBuffer ,然后進行ROI裁剪操作,將ROI圖像bitmap傳給ShowProcessedImage函數進行處理”。getRoiBmpData函數被5個回調函數同時調用,因為有5個相機,每個相機調用一個回調函數。那么我為什么5個相機要用5個回調呢,因為這樣每個回調函數只負責一個相機,這樣看起來分工更明確。

現在有兩個問題:
問題1.這行代碼“ Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);”中bitmap 被反復創建和使用,在該函數中需要手動釋放嗎?

問題2.這行代碼“ Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);”中bitmap 被反復創建和使用,那么我可以創建一個全局變量Bitmap bitmap ,這樣就不用每次進入函數都會創建一次bitmap ,但是呢我擔心5個回調函數共享這一個bitmap 會沖突嗎?當然ShowProcessedImage函數和getRoiBmpData函數也只有一個,都是同時被5個回調函數調用的。

8.1 被反復創建和使用,需手動釋放嗎?

在這段代碼中,雖然 bitmap 被反復創建和使用,但在每次使用后都被正確地釋放了。這是因為在使用完畢后調用了 bitmap.UnlockBits(bmpData); 來釋放 BitmapData 對象。所以不需要手動釋放 bitmap,因為在 bitmap 超出范圍時會被自動回收。

bitmap 對象是在函數內部創建的,它會在函數執行完畢后自動離開作用域,從而被垃圾回收機制回收。因為它的生命周期受到函數作用域的限制,一旦函數執行完畢就會被銷毀。

8.2 創建一個全局Bitmap bitma,多線程訪問會沖突嗎?

問題2:創建一個全局變量 Bitmap bitmap 是一種有效的方法,可以避免在每次函數調用時都重新創建 bitmap 對象。然而,如果多個回調函數同時訪問和修改同一個全局 bitmap 對象,就可能會發生競態條件或數據競爭,導致程序行為不確定或產生錯誤。因此,你需要確保在對 bitmap 進行讀寫操作時進行適當的線程同步,以避免沖突。

為了解決這個問題,可以采取以下方法之一

  • 在訪問全局 bitmap 對象時使用線程同步機制(如鎖),以確保在任何時候只有一個回調函數可以訪問或修改 bitmap 對象。這樣可以避免并發訪問導致的問題。一個簡單的方法是在訪問 bitmap 之前使用 lock 關鍵字來確保線程安全,就像你在代碼中對關鍵部分加鎖一樣。這樣可以確保每次只有一個線程能夠訪問 bitmap,從而避免并發沖突。

  • 將 bitmap 對象作為函數參數傳遞給每個回調函數,這樣每個函數都有自己的 bitmap 對象實例,不會相互干擾。

選擇哪種方法取決于你的具體需求和代碼結構。如果需要在多個回調函數之間共享相同的 bitmap 對象,并且需要確保線程安全,則使用第一種方法;如果每個回調函數都需要獨立的 bitmap 對象,則使用第二種方法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917222.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917222.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917222.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ubuntu22.04系統入門 linux入門(二) 簡單命令 多實踐以及相關文件管理命令

以下有免費的4090云主機提供ubuntu22.04系統的其他入門實踐操作 地址&#xff1a;星宇科技 | GPU服務器 高性能云主機 云服務器-登錄 相關兌換碼星宇社區---4090算力卡免費體驗、共享開發社區-CSDN博客 之所以推薦給大家使用&#xff0c;是因為上面的云主機目前是免費使用的…

分布式ID方案(標記)

一、參考文章-標記 分布式ID方案有哪些&#xff1f;雪花算法如何搞定時鐘回撥和動態機器ID&#xff1f; 二、應用 1.百度 uid-generator github項目地址 原理參考 2.百度 uid-generator 擴展應用 燈官網 燈 項目代碼 lamp-util 單元模塊 lamp-util 單元模塊子模塊 lamp-…

std::map 加鎖

在并發環境下使用std::map&#xff0c;必須采取同步措施。 在并發環境下對 std::map 進行不加鎖的讀寫操作會導致嚴重的線程安全問題&#xff0c;主要會產生以下幾種問題&#xff1a; ?? 主要風險與后果數據競爭&#xff08;Data Race&#xff09; 當多個線程同時修改同一個鍵…

學習筆記090——Ubuntu 中 UFW 防火墻的使用

文章目錄1、允許特定的端口訪問2、允許特定 IP 訪問某個端口3、允許某個范圍的端口4、查看 UFW 狀態5、重新加載 UFW6、啟用 UFW7、關閉 UFW1、允許特定的端口訪問 # 允許 TCP 端口&#xff08;例如 80&#xff09;&#xff1a; sudo ufw allow 80/tcp# 允許 UDP 端口&#xf…

移動端 WebView 內存泄漏與性能退化問題如何排查 實戰調試方法匯總

在混合 App 應用中&#xff0c;WebView 頁面常承載復雜業務邏輯與交互。隨著用戶使用時間增長&#xff0c;特別在切換多個頁面或反復打開界面后&#xff0c;常常會出現性能下降、頁面卡頓、甚至白屏崩潰等現象。這通常是因為頁面存在內存泄漏、事件監聽未解綁或垃圾回收阻塞導致…

JSON 對象在瀏覽器中順序與后端接口返回不一致的問題

一、問題描述 后端接口返回一個字典表的JSON對象&#xff0c;頁面展示排序與預期排序不一致。 在瀏覽器調試面板Response中看到接口原始響應字符串&#xff0c;是期望順序&#xff1a;在Preview中看到&#xff0c; key “22” 被提到最前&#xff0c;順序發生變化&#xff1a;頁…

Spring MVC數據傳遞全攻略

Spring MVC數據傳遞一、前端到后端的數據傳遞1. 使用 RequestParam 傳遞簡單參數2. 使用 PathVariable傳遞路徑參數3. 使用RequestBody傳遞 JSON 數據二、后端到前端的數據傳遞1. 使用Model或 ModelAndView傳遞數據到前端2. 使用HttpServletResponse直接寫回數據3.使用Response…

倉庫管理系統-12-前端之頭部區域Header基于嵌套路由訪問個人中心

文章目錄 1 個人中心 1.1 DateUtils.vue(子組件) 1.2 Home.vue(父組件) 1.3 router/index.js(嵌套路由) 1.4 index.vue(路由占位符) 2 Header.vue 2.1 頁面布局 2.2 toUser方法 2.3 初始加載 2.4 Header.vue 頭部區域Header中有一個個人中心下拉菜單,點擊個人中心選項,通過嵌…

【智能協同云圖庫】第七期:基于AI調用阿里云百煉大模型,實現AI圖片編輯功能

摘要&#xff1a;AI 高速發展賦能傳統業務&#xff0c;圖庫網站亦有諸多 AI 應用空間。以 AI 擴圖功?能為例&#xff0c;讓我們來學習如何在項目?中快速接入 AI 繪圖大模型。?用戶可以選擇一張已上傳的圖片&#xff0c;?通過 AI 擴圖得到新的圖片&#xff0c;希望可以幫到大…

Notepad++插件安裝

方式一&#xff1a;自動安裝&#xff08;有些notepad并不好用&#xff0c;推薦方式二&#xff09;工具欄-》插件-》插件管理如下點擊安裝后會提示&#xff0c;后端安裝&#xff0c;安裝成功后自動啟動&#xff0c;本人使用的v8.6.4的版本&#xff0c;插件基本都無法自動安裝&am…

git pull和git fetch的區別

git pull和git fetch是git版本控制系統中的兩個基本命令&#xff0c;它們都用于從遠程倉庫更新本地倉庫的信息&#xff0c;但執行的具體操作不同。git fetch:git fetch下載遠程倉庫最新的內容到你的本地倉庫&#xff0c;但它并不自動合并或修改你當前的工作。它取回了遠程倉庫的…

Item35:考慮virtual函數以外的其他選擇

在C++中,虛函數是實現多態的傳統方式,但并非唯一選擇。過度依賴虛函數可能導致派生類與基類的強耦合,或難以在運行時靈活切換行為。《Effective C++》Item35指出:應根據場景選擇更合適的替代方案,包括NVI模式、函數指針、策略模式等。本文解析這些方案的原理、適用場景及實…

Vue3 狀態管理新選擇:Pinia 從入門到實戰

一、什么是pinia? 在 Vue3 生態中&#xff0c;狀態管理一直是開發者關注的核心話題。隨著 Vuex 的逐步淡出&#xff0c;Pinia 作為官方推薦的狀態管理庫&#xff0c;憑借其簡潔的 API、強大的功能和對 Vue3 特性的完美適配&#xff0c;成為了新時代的不二之選。今天我們就來深…

Unity相機控制

相機的控制無非移動和旋轉&#xff0c;每種操作各3個軸6個方向&#xff0c;一共12種方式。在某些需要快速驗證的項目或Demo里常常需要絲滑的控制相機調試效果。相機控制雖然不是什么高深的技術&#xff0c;但是要寫的好用還是很磨人的。 鎖定Z軸的旋轉 一個自由的相機可以繞 …

vue2 使用liveplayer加載視頻

vue2 使用liveplayer加載視頻 官網: https://www.liveqing.com/docs/manuals/LivePlayer.html支持WebRTC/MP4播放;支持m3u8/HLS播放;支持HTTP-FLV/WS-FLV/RTMP播放;支持直播和點播播放;支持播放器快照截圖;支持點播多清晰度播放;支持全屏或比例顯示;自動檢測IE瀏覽器兼容播放;支…

JavaScript語法樹簡介:AST/CST/詞法/語法分析/ESTree/生成工具

AST簡介 在平時的開發中&#xff0c;經常會遇到對JavaScript代碼進行檢查或改動的工具&#xff0c;例如ESLint會檢查代碼中的語法錯誤&#xff1b;Prettier會修改代碼的格式&#xff1b;打包工具會將不同文件中的代碼打包在一起等等。這些工具都對JavaScript代碼本身進行了解析…

Java函數式編程之【基本數據類型流】

一、基本數據類型與基本數據的包裝類 在Java編程語言中&#xff0c;int、long和double等基本數據類型都各有它們的包裝類型Integer、Long和Double。 基本數據類型是Java程序語言內置的數據類型&#xff0c;可直接使用。 而包裝類型則歸屬于普通的Java類&#xff0c;是對基本數據…

.NET Core部署服務器

1、以.NET Core5.0為例&#xff0c;在官網下載 下載 .NET 5.0 (Linux、macOS 和 Windows) | .NET 根據自己需求選擇x64還是x86&#xff0c;記住關鍵下載完成還需要下載 Hosting Bundel &#xff0c;否則不成功 2、部署https將ssl證書放在服務器上&#xff0c;雙擊導入&#…

YOLO---04YOLOv3

YOLOV3 論文地址&#xff1a;&#xff1a;【https://arxiv.org/pdf/1804.02767】 YOLOV3 論文中文翻譯地址&#xff1a;&#xff1a;【YOLO3論文中文版_yolo v3論文 中文版-CSDN博客】 YOLOv3 在實時性和精確性在當時都是做的比較好的&#xff0c;并在工業界得到了廣泛應用 …

Qt知識點3『自定義屬性的樣式表失敗問題』

問題1&#xff1a;自定義類中的自定義屬性&#xff0c;如何通過樣式表來賦值除了QT自帶的屬性&#xff0c;我們自定義的類中如果有自定義的靜態屬性&#xff0c;也可以支持樣式表&#xff0c;如下 &#xff1a; Q_PROPERTY(QColor myBorderColor READ getMyBorderColor WRITE s…